Load Building Outbound multi-temp loads, proven legal on six hard constraints before a trailer leaves the dock
What this demo proves
Outbound load building does not have a prediction problem. By the time a trailer is staged, the orders are picked and counted to the case. The open question is not how much will ship, it is whether the way we packed and routed it is actually legal. This demo answers that question with a proof rather than a hope: every trailer load is checked against six independent hard constraints, and no load is allowed to depart until it passes all six. The route optimizer proposes consolidations; the constraint engine is the authority that accepts or rejects them. The optimizer is checked, never trusted.
The contrast that makes the point is a legacy cube-only load builder: it merges stops whenever the total volume fits inside the trailer and ignores everything else. Run side by side on the same orders, it ships loads that overrun the frozen compartment, exceed the axle weight limit, crush fragile product, or miss delivery windows. The verified builder refuses those same merges and logs exactly why.
How it works
The pipeline has four stages, all deterministic from a seed:
- Known orders in.
generate_orders(network, scenario, seed)produces per-store order lines with exact case counts. This is post-pick reality, not a forecast. - Clarke-Wright savings merges. The builder starts with one load per store, then walks candidate merges in descending order of routing savings (the classic depot-distance savings heuristic, adapted so the unit being merged is a store stop on a multi-temperature trailer). A store whose own order overruns a compartment is first split by temperature class into feasible pieces.
- Every merge checked by the constraint engine. A merge is accepted
only if
LoadConstraintEngine.check_loadreturns feasible on the combined load. This is the crucial difference from ordinary savings routing: the savings heuristic proposes, the six-rule engine disposes. - Rejected merges logged, certificates issued. Every refused merge is recorded with the single rule that blocked it and the measured numbers. Every accepted load is issued a certificate: the full enumeration of all six rules with their measured and limit values, plus a hash of the load.
The six rules
Each rule is a small, independent, named check in
grocery_demos/core/constraints.py. The engine always returns all six, pass
or fail, each with a human-readable detail string and the measured value against its
limit. The example detail strings below are real output from a seed-42 run.
| Rule | What it enforces | Example detail (seed 42) |
|---|---|---|
temp_zone_integrity |
Every line rides in the compartment matching its temperature class, and each compartment stays within its cube and weight capacity (frozen 550 ft³ / 9,000 lb, refrigerated 900 ft³ / 14,000 lb, ambient 1,500 ft³ / 22,000 lb). | frozen: 855/550 ft3 OVER; frozen: 10841/9000 lb OVER |
payload_weight |
Total case weight across all compartments stays within the trailer payload limit of 43,000 lb. | total 49625/43000 lb OVER |
stacking_safety |
Fragile cases (crush class 3: bread, eggs, chips, produce) need top-tier headroom. Each compartment must fit its normal cube plus a 30% surcharge on fragile cube. | ambient: 535 fragile cases need 190 ft3 top-tier; 110 ft3 free |
delivery_windows |
Simulate the route from the DC at 06:00, driving at 45 mph and unloading 30 to 45 minutes per stop; every arrival must fall inside the store's delivery window. Early arrivals wait. | Store 117: arrive 4.63h after close 4.0h |
route_duration_hos |
Drive time plus unload plus wait plus the return leg to the DC must fit the 10-hour hours-of-service limit. Guards long multi-stop routes. | route 4.65/10.0 h within (passing; the limit binds on longer routes) |
unload_accessibility |
Last-in-first-out pallet access: at most three stops per trailer, and a tight-dock store may not sit behind other stops (it must be delivered first). | Store 115 is tight-dock but stop 2, not first |
Scenarios and their seed-42 results
Four order scenarios stress different failure modes. The numbers below are the actual
output of running the pipeline at seed 42. "Verified trailers" is the load
count the verified builder produces; "avg cube utilization" is filled volume over total
trailer volume averaged across those trailers; "naive violating loads" is how many of the
cube-only builder's loads fail at least one rule.
| Scenario | Verified trailers | Avg cube util | Verified miles | Naive violating loads | Rules the naive loads break |
|---|---|---|---|---|---|
weekdaybaseline volume |
19 | 74.6% | 113.6 | 7 | unload_accessibility (7), delivery_windows (3) |
weekend_peakproduce and dairy heavy |
34 | 50.9% | 179.5 | 14 | stacking_safety (14), temp_zone_integrity (11), unload_accessibility (5), delivery_windows (2) |
holiday_surgesome stores exceed one trailer |
43 | 53.9% | 290.3 | 12 | temp_zone_integrity (12), stacking_safety (12), payload_weight (5) |
frozen_promofrozen lines tripled |
36 | 34.1% | 188.6 | 16 | temp_zone_integrity (16), stacking_safety (16), unload_accessibility (7), delivery_windows (3) |
Counts per rule add to more than the violating-load total because a single illegal load
usually breaks several rules at once. In every scenario the verified builder produces a
plan that is fully feasible and covers every order line exactly, while the cube-only
builder ships between 7 and 16 illegal loads. frozen_promo is the sharpest
illustration: tripling frozen volume makes cube-only merges overrun the 550 ft³
frozen compartment specifically, so 16 of the 17 cube-only loads are illegal.
Reading the UI
The live page at /grocery/loadbuild has six tabs:
- Scenario. Pick one of the four order books, set a seed, and run. The result summary shows the headline KPIs: trailers used, average cube utilization, windows met, total miles, and the naive violation count.
- Orders. The order book folded into verified loads, one row per trailer, with lines, cases, cube utilization, weight, and a legal or illegal badge.
- Loads. One card per trailer. Three horizontal fill bars show the frozen, refrigerated, and ambient compartments as a percentage of cube capacity; a bar that would exceed 100% turns red. A fourth bar shows payload weight against the 43,000 lb limit. Below the bars, the stop list gives each store's arrival time and its delivery window in clock time, with a tight-dock badge where it applies. A row of pass/fail chips shows the verdict on all six rules at a glance.
- Routes. An SVG map of the regional DC (red) and the stores, with one colored polyline per trailer route. Tight-dock stores are marked in orange.
- Certificates. The full six-rule enumeration for every load, with measured and limit numbers and the input hash (described below).
- Comparison. The story tab. It lists the illegal loads the cube-only builder would have shipped, each with the failed rule and its numbers, against the verified builder's zero violations, and shows the trailer and mile cost of that safety. It also surfaces a sample of the rejected-merge log.
Reading the rejected-merge log
Each entry names the stores the builder tried to combine, the single rule that blocked the merge, and the measured numbers, for example: merge of Store 116 + Store 123 rejected -- temp_zone_integrity: frozen 855/550 ft3 OVER. This log is the demo's central artifact. It is the running record of the optimizer being overruled, and it turns "the system is safe" into a list of specific consolidations that were refused and the exact limit each one would have broken. At seed 42 the four scenarios log between 78 and 908 rejected merges each; the UI shows a readable sample and reports the full count.
The certificate
Every accepted load is issued a certificate by
grocery_demos/core/certificates.py. It contains:
load_idandtrailer_id, the stop sequence, and the line count.rules: all six rule results, each with its name, pass or fail flag, human detail string, and the measured value against its limit.feasible: the overall verdict.input_hash: a SHA-256 over a canonical, order-independent JSON of the load (trailer, stops, and sorted lines). A reviewer can recompute this hash from the load to confirm the certificate covers that exact load and nothing was altered after the fact.engine_version:grocery-lce-1.0, so a certificate is tied to the ruleset that produced it.
Honest caveats
- The network, SKUs, and orders are synthetic and deterministic, generated from a seed and deliberately tuned so the failure modes are visible in a short demo. The real artifact is the constraint engine, the rejected-merge log, and the certificates, not the data.
- The comparison baseline is an honest cube-only load builder: it merges on total volume alone, which is what a legacy system without constraint composition effectively does. It is not a strawman with the rules deleted at random; it is the specific, common failure of checking volume and nothing else.
- The routing is Clarke-Wright savings, a heuristic, not an optimal solver. It produces good consolidations quickly, not provably minimal fleets. The claim of this demo is about legality of the loads it does build, not optimality of the fleet size.
Related
Try it live
Run the four scenarios, watch merges get rejected, and read the certificates.
Suite overview
How load building fits with the inbound and execution demos, and the shared synthetic world they run on.
Sibling demos
Replenishment adds forecast guardrails; re-planning re-verifies after a mid-day disruption.