Skip to content

MEIO Lateral Backup, Hierarchical Replenishment, and Repair Flows

Status: Design review and algorithm hardening proposal. No implementation is implied by this document.

Scope: How MEIO should size inventory when a network contains ordinary hierarchical replenishment, lateral or mutual stock backup, and locations that can repair returned items.

Code examined: files/meio_runner.py, files/MEIO_v3/src/sku.rs, files/MEIO_v3/src/marginal.rs, files/MEIO_v3/src/optimizer.rs, files/meio_zone_pass.py.

Executive Verdict

The proposed direction is correct: MEIO needs to distinguish a one-way replenishment hierarchy from faster lateral rescue between locations. It also needs to treat repair as a supply process with its own lead time rather than as an instantaneous demand reduction.

The proposed formula is not yet safe enough to implement as written. In particular:

  1. A fill rate is not the probability that a peer has surplus stock available for another location.
  2. Demand share is not, by itself, a valid probability that a particular shortage will be rescued.
  3. Lateral, hierarchical, and repair supply can run concurrently. Their arrival time is usually a minimum, not a mutually exclusive mixture.
  4. A peer's stock cannot be credited independently to every claimant without modelling shared quantity and allocation priority.
  5. FR * dilution can produce plausible numbers while violating the actual inventory state.
  6. A bounded two-pass fixed point is not a correctness guarantee for cyclic mutual dependencies.
  7. The current Rust RepairFlow.repair_tat_mean field is deserialized but is not used by the main compute_mv_recursive service-level calculation. The present MEIO optimizer therefore does not truly price repair lead time in its main marginal-value path.

The safest design is a layered network model:

flowchart LR
    D[Demand at location] --> O[Own good stock]
    O --> S[Service result]
    H[Hierarchical replenishment] --> S
    L[Lateral backup / peer rescue] --> S
    R[Repair output] --> S
    B[Bad returns] --> Q[Repair queue]
    Q --> R
    H --> H2[Upstream stock]
    L --> L2[Peer stock and lane capacity]

The optimization objective should evaluate service after all eligible supply processes compete for the same demand. Inventory cost is paid once. Service benefits are counted once. This removes the current risk of giving one unit of stock full credit for every downstream or peer claimant.

1. The Three Different Questions

The original proposal combines three related but different questions. They should remain separate in the model.

1.1 Who is allowed to supply whom?

This is a topology and policy question.

  • A warehouse may replenish a store through a TRANSFER route.
  • Two stores may rescue each other through explicit lateral routes.
  • A repair centre may return a repaired item to one or more eligible locations.
  • A location may be physically connected to another location but not be authorized to consume its stock.
  • A route may exist in the database but be inactive, capacity-limited, item-incompatible, or unavailable for emergency rescue.

The answer must come from explicit directed edges and policy, not from the existence of a site or a vague assumption that every location can borrow from every other.

1.2 How much can a source supply?

This is a state and allocation question.

  • A source may have stock, but it may need to protect its own demand.
  • A source may already have promised stock to another destination.
  • A source may have enough units for one claimant but not three.
  • A source may have a repair pool, but the pool may be empty or already reserved.
  • A route may have a weekly capacity or a transport blackout.

The answer is not represented by the source fill rate alone.

1.3 When does the supply arrive?

This is a timing question.

  • Own stock serves immediately.
  • Lateral transfer may arrive in 0.25 weeks.
  • A repair return may arrive in 3.5 weeks.
  • Hierarchical replenishment may arrive in 4 weeks.
  • An emergency purchase may arrive in 6 weeks.

When several branches run in parallel, the demand sees the first feasible arrival. This is usually a minimum over competing arrival times, not a single branch selected before the demand is known.

2. What the Current MEIO Path Actually Represents

2.1 Hierarchical propagation

The Rust optimizer currently carries repl_site_ids as a list of site IDs on each SKU. The recursive path in files/MEIO_v3/src/marginal.rs:803-828 treats this list as the locations replenished by the current SKU. It computes a parent wait using:

parent_wait = parent_total_lead_time * (1 - parent_fill_rate)

and passes that wait to each dependent SKU.

The PostgreSQL CTE in files/meio_runner.py:1515-1529 currently groups transfer routes by destination and aggregates source_site_id. That produces a list of upstream sources on the destination SKU, while the Rust recursion expects downstream dependants on the source SKU. The field contract and loader direction therefore disagree.

This is a prerequisite defect, independent of mutual rescue. The first corrective test should use a simple warehouse-to-store chain and prove that investing in the warehouse changes the store's wait, while investing in the store does not change the warehouse's replenishment wait unless a separate reverse or lateral route exists.

2.2 Current topology is too small for lateral rescue

SkuRecord.repl_site_ids contains only site IDs. It does not carry:

  • route ID;
  • source and destination item identity;
  • transshipment lead-time distribution;
  • route capacity;
  • route eligibility for emergency rescue;
  • route coverage or allocation policy;
  • route reliability or disruption state;
  • whether the edge is hierarchical, lateral, repair, or emergency;
  • whether a route can be used in both directions.

That representation can support a simple tree-like dependency, but not a faithful mutual network. A lateral implementation should not overload this list with hidden semantics.

2.3 Current repair representation

RepairFlow in files/MEIO_v3/src/sku.rs:24-39 contains:

  • return rate;
  • repair yield;
  • repair turnaround mean and coefficient of variation;
  • WIP quantity.

The Python loader in files/meio_runner.py:2099-2209 merges one selected RETURN route and one selected REPAIR route per (item_id, site_id). It combines return transit and shop time into repair_tat_mean.

The main marginal calculation in files/MEIO_v3/src/marginal.rs:542-552 uses return rate and yield to reduce effective demand and uses wip_qty * repair_yield as an on-hand credit. It does not use repair_tat_mean in that calculation. The turnaround field exists on the wire, but the ordinary MEIO marginal service calculation does not make the repaired unit compete against lateral or hierarchical supply according to its arrival time.

This is a critical distinction:

returned and eventually repairable != immediately available good stock

The separate bad/repair forecast and supply engines do model repair arrivals, but MEIO sizing must use the same timing semantics if repair is intended to reduce required good stock.

2.4 The zone pass is not the same feature

files/meio_zone_pass.py creates a virtual pooled SKU in pass 1 and applies a group budget cap to physical members in pass 2. This is useful for an explicitly declared collaborative zone. It is not a general model of dynamic peer rescue.

Do not automatically combine these two effects:

  • zone pooling says a defined set of locations shares a planned inventory envelope;
  • lateral rescue says a shortage may use a peer's currently available surplus through a route and policy.

If both are credited independently, the same risk-pooling benefit can be counted twice. A later design may use the zone result as a prior, reserve, or capacity envelope for the lateral model, but it must not be another independent service benefit.

3. Correct Mental Model: A Time-Expanded Supply Graph

The most important design choice is to represent supply as a directed, typed graph over time.

3.1 Nodes

A node should be at least:

(item_id, site_id, condition, time)

Useful conditions include:

  • good stock available for service;
  • bad or unserviceable stock awaiting return or repair;
  • repair_wip currently in a repair process;
  • reserved stock committed to a priority or firm order;
  • in_transit stock that cannot yet serve.

The condition dimension prevents good stock, bad stock, and WIP from being silently mixed.

3.2 Edge classes

Every edge should have an explicit edge_kind.

Edge class Direction Typical time Can it rescue immediately? Main constraint
FORWARD_REPLENISHMENT source to destination weeks No, unless already in stock supplier capacity and route LT
LATERAL_RESCUE peer to peer hours to days Yes, after transport free surplus, lane capacity, policy
EMERGENCY_REPLENISHMENT source to destination days to weeks Sometimes premium capacity and cost
RETURN demand site to return facility days to weeks No bad-stock flow and transport
REPAIR_PROCESS repair facility input to output days to weeks No repair capacity, yield, queue
REPAIR_DISTRIBUTION repair facility to eligible site days to weeks Only if already repaired and stocked output allocation and route capacity
BOM_COMPONENT component to assembly dependency depends on supply No direct stock movement all required components

The same physical route may have more than one business use. For example, a regular transfer lane may be available for forward replenishment but forbidden for emergency lateral rescue. That should be represented by eligibility flags, not inferred from route type.

3.3 Directed does not mean hierarchical

Mutual backup is often bidirectional in business policy, but it is still represented by two directed edges:

L1 --0.25 week, cap 10--> L2
L2 --0.50 week, cap 5 ---> L1

The two edges can have different transport times, costs, capacities, customs constraints, and operating windows. Never create a reverse edge automatically unless the reverse permission is explicit.

4. Why the Proposed FR * dilution Formula Is Unsafe

The proposal defines:

a(j -> i) = FR(j) * d(j -> i)
d(j -> i) = demand_i / sum(demand_of_claimants_of_j)

and then:

r_i = 1 - product_j(1 - a(j -> i))

This is an understandable first approximation, but it has four structural problems.

4.1 Fill rate is not spare-stock availability

FR(j) = 0.90 usually means that 90 percent of demand is served according to a service definition. It does not mean that 90 percent of the time peer j has a free unit that can be dispatched to i.

Two locations can both have a 0.90 fill rate and radically different rescue capability:

Peer state Own fill rate Free units after own protection Rescue probability for a 10-unit request
Just enough stock for its own demand 0.90 0 0.00
Large planned reserve 0.90 often 10+ potentially high
High fill rate caused by very low demand 0.90 volatile unknown
Stock physically present but reserved for a critical customer 0.90 0 for this claimant 0.00

The correct quantity is a conditional free-stock distribution, not the source fill rate.

4.2 Demand share is not a rescue probability

Demand share may be a reasonable allocation fraction under a specific proportional-fairness policy. It is not automatically the probability that a unit reaches a claimant.

The result depends on:

  • whether shortages occur simultaneously;
  • whether criticality or customer priority dominates fairness;
  • whether one claimant needs 1 unit and another needs 100;
  • whether the source allocates by FIFO, priority, proportional share, or reservation;
  • whether the source has enough free stock for one request or all requests;
  • whether the route capacity is exhausted before the request arrives.

If demand share is used, it must be documented as an allocation policy and must satisfy source-level conservation constraints.

4.3 The formula ignores quantity

The probability of supplying one unit is not the probability of supplying 100 units. Define a request quantity q and use either:

p(j -> i, q) = P(free_stock_j >= q and lane_is_available)

or, preferably, the expected useful quantity:

u(j -> i, q) = E[min(q, allocatable_free_stock_j) * lane_reliability]

The latter behaves correctly when a peer can partially rescue a shortage.

4.4 The product assumes independence and no shared capacity

The product form is valid only under strong assumptions:

  • peer availability events are conditionally independent;
  • each peer has independent stock;
  • route capacities do not interact;
  • one claimant's use does not reduce another claimant's opportunity;
  • no common-cause disruption affects several peers;
  • allocation decisions do not depend on the same shortage state.

Those assumptions are often false in a distributed network. A correlation discount of 1 - rho is a useful conservative experiment, but it is not a statistically correct replacement for the joint distribution. It can also produce a number that is neither calibrated nor conserved.

5. Corrected Analytic Approximation

An analytic approximation can still be useful for an initial release if its limits are explicit.

5.1 Free-stock availability

For source j, destination i, request quantity q, and rescue deadline h, define:

F_j(q, h) = P(
    source_j_has_at_least_q_free_units_at_decision,
    route_j_to_i_arrives_by_h,
    request_i_is_eligible,
    allocation_policy_awards_i_the_quantity
)

Then use:

a(j -> i, q, h) = F_j(q, h)

If a simplified factorization is necessary:

a(j -> i, q, h)
  = p_free_j(q)
  * p_lane_ji(h)
  * p_allocate_ji(q | shortage_state)

The allocation factor must be state-aware and source-conserving. It must not be a constant demand share unless that is the declared policy.

5.2 Rescue probability under limited assumptions

For independent rescue edges only:

r_i(q, h) = 1 - product_j(1 - a(j -> i, q, h))

This should be described as the probability of at least one qualifying rescue opportunity, not as a universal truth about the network.

The expected useful quantity is usually more stable:

U_i(q, h) = E[quantity served by eligible peers by h]

The service model can then use U_i to reduce shortage quantity rather than treating rescue as a binary event.

5.3 Arrival-time competition

The original proposal uses:

expected_wait = (1 - own_FR) * (
    no_rescue_probability * forward_LT
    + rescue_probability * lateral_LT
)

This is only valid when the forward and lateral branches are exclusive choices. If they run in parallel, use:

T_i = min(
    T_lateral_i,
    T_forward_i,
    T_repair_i,
    T_emergency_i
)

where an unavailable branch has T = infinity, and a branch can arrive only if it has enough quantity and capacity. The service result is then evaluated at the demand's required date.

For a simple two-branch example:

Branch Probability available Arrival time
Peer rescue 0.60 0.25 weeks
Warehouse replenishment 1.00 2.00 weeks

If peer rescue and warehouse replenishment run concurrently, the expected arrival time is:

0.60 * 0.25 + 0.40 * 2.00 = 0.95 weeks

The original mixture gives the same number in this special case. But if warehouse supply also arrives at 2 weeks when rescue succeeds, the correct arrival is still 0.25 weeks for rescued demand and the warehouse stock is available for later demand. A scalar wait-time update loses that inventory trajectory and can over-credit later shortages.

6. Worked Examples

6.1 Example 1: the fourth-location inconsistency

The proposal says that a fourth location joins without adding stock, but then assigns it FR = 0.90. Those statements cannot both be true.

Case A: L4 joins with its own existing stock

All four locations have FR = 0.90 and equal demand. With the proposed approximation:

d = 1/3
a = 0.90 / 3 = 0.30
r_L1 = 1 - (1 - 0.30)^3 = 0.657

This is the proposal's calculation. But L4 did add a stock source to the network, even if no new stock was purchased by the optimizer.

Case B: L4 joins as a claimant but has no stock

L4 has no rescue stock, so its peer availability is zero. If L2 and L3 still have stock and the claimant set includes L4:

d = 1/3
a_L2_to_L1 = a_L3_to_L1 = 0.90 / 3 = 0.30
a_L4_to_L1 = 0
r_L1 = 1 - (0.70 * 0.70 * 1.00) = 0.51

Adding a claimant without stock can reduce each source's allocation share. That may be the desired policy result, but it must be made explicit. It is not the same experiment as adding a fully stocked fourth location.

6.2 Example 2: equal fill rate, zero rescue

L1 and L2 both have demand 100 units/week and fill rate 0.90. Each location carries exactly the stock needed for its own service target. There is no spare reserve, and both locations have a firm local priority.

FR_L2 = 0.90
free_stock_L2 = 0
a_L2_to_L1 = 0

The proposed FR * d formula would give 0.90 in a two-node network. It would credit a rescue that cannot happen. A free-stock calculation gives zero.

6.3 Example 3: one peer can partially rescue several claimants

L2 has 12 free units. L1 needs 10 units, L3 needs 10 units, and the lane from L2 has capacity 15 units this week.

Under a proportional policy:

allocation_share_L1 = 10 / (10 + 10) = 0.5
allocation_share_L3 = 0.5
expected_alloc_L1 = 6 units
expected_alloc_L3 = 6 units

Neither claimant is rescued in the binary sense, but both receive useful quantity. A binary product model loses this information. The inventory-service objective should measure the quantity served, or use a quantity-specific service definition.

6.4 Example 4: hierarchy plus lateral rescue

Network:

WH --2.0 weeks--> S1
WH --2.0 weeks--> S2
S1 --0.25 weeks, cap 8--> S2
S2 --0.50 weeks, cap 4--> S1

S1 has a shortage of 6 units at week 0.5. S2 has 5 free units and its lane to S1 has capacity 4. WH has 20 units arriving at week 2.

The correct service result is:

  • 4 units from S2 arrive at week 1.0;
  • 2 units remain short until the warehouse arrival at week 2.0, if the warehouse allocation policy awards them;
  • S2's source stock is reduced by 4 units;
  • the same 4 units cannot also be credited to S2's own future demand.

Investing in S2 has two effects:

  1. It can improve S2's own fill rate.
  2. It can improve S1's service, but only up to the lateral lane capacity and only after protecting S2's own demand.

The marginal value of S2 is therefore a network objective, not merely S2's local fill-rate delta.

6.5 Example 5: repair competes with lateral supply

S1 has a shortage at week 0. There are two possible supply branches:

S2 lateral transfer: 0.25 weeks, 70% probability of 5 free units
Repair centre return: 3.5 weeks, 80% yield, 90% probability of 10 recoverable units
WH replenishment: 2.0 weeks, 100% planned arrival of 10 units

For the first 5 units, lateral rescue is the earliest branch when available. If it is unavailable, the warehouse arrives at 2 weeks before repair at 3.5 weeks. Repair may still replenish a later shortage or cover a warehouse delay.

The branch ordering is not:

70% lateral + 30% repair

It is a time-ordered competition:

first feasible arrival = min(lateral arrival, warehouse arrival, repair arrival)

The repair stream must also consume bad units, pass through a capacity queue, and apply yield. It cannot be treated as an unconditional 80% supply probability.

6.6 Example 6: repair facility shared by two sites

S1 and S2 each return 10 bad units/week to repair centre R. R has repair capacity 12 units/week and yield 0.8. The total recoverable output is:

min(10 + 10, 12) * 0.8 = 9.6 good units/week

It is not 8 units for S1 plus 8 units for S2. The repair facility is shared and capacity-limited. An allocation policy is required:

  • criticality first;
  • proportional allocation;
  • FIFO by return date;
  • site reservation quotas;
  • minimum service guarantee plus residual pooling.

Without that policy, MEIO cannot determine which site receives the 9.6 units.

6.7 Example 7: repair lead time changes the correct stock decision

Two otherwise identical locations have demand 10/week and a 95% target.

Location Return rate Yield Repair cycle WIP
A 20% 90% 1 week 5
B 20% 90% 8 weeks 5

Both have the same long-run repair supply rate:

10 * 0.20 * 0.90 = 1.8 units/week

But A receives repair output quickly and B carries a much larger in-process pipeline. A planner should not size them identically. The WIP quantity must be time-phased:

usable_WIP_by_horizon = WIP units expected to finish and arrive before the shortage deadline

Crediting all WIP * yield immediately makes B look artificially well supplied.

6.8 Example 8: common-cause disruption

Four stores are geographically separate in normal weeks, but a regional transport strike disables all lateral routes at once. Pairwise historical demand correlation may be close to zero, yet rescue availability is perfectly correlated during the strike.

A demand-correlation discount cannot model this. The model needs a common-cause scenario such as:

P(normal week) = 0.97
P(regional disruption) = 0.03
P(all lateral lanes available | disruption) = 0

The optimizer should reserve enough local or hierarchical stock if the business target includes disruption weeks.

7. Repair Must Be a First-Class Flow

7.1 Separate the repair stages

A repairable item should pass through explicit stages:

flowchart LR
    G[Good item at demand site] --> X[Failure / bad event]
    X --> B[Bad item at site]
    B --> T[Return transit]
    T --> R[Repair facility queue]
    R --> P[Repair process]
    P --> Y{Yield}
    Y -->|good| O[Good output pool]
    Y -->|scrap| Z[Loss / replacement demand]
    O --> D[Distribution to eligible sites]

Each stage can have its own lead time, capacity, failure probability, and inventory state.

7.2 Repair parameters required for network MEIO

At minimum, the model needs:

Parameter Meaning
return source and destination where bad stock originates and where it is processed
repair facility identity shared pool key for capacity and WIP
return lead-time distribution time for bad item to reach repair
queue or shop lead-time distribution time in the repair process
repair yield probability or fraction becoming good
repair capacity units per period, possibly by item family
current WIP by age units in repair and their remaining-time distribution
good-output allocation policy which sites receive repaired units
output route lead time time from repair facility to demand site
substitution or compatibility whether repaired item can serve another item/site
common-cause availability whether repair or transport can be disrupted

The current single RepairFlow object is not sufficient for multiple repair centres, alternative routes, shared queues, or repair output pooling.

7.3 Do not net repair twice

There are two valid ways to represent repair supply:

  1. Reduce demand using an analytically derived net repair rate.
  2. Add time-phased repair arrivals to the supply process.

Using both for the same units double counts repair.

The recommended design is to use time-phased repair arrivals in the scenario/service engine. A net-rate approximation may remain as an explicitly named legacy mode, but it should not be silently combined with repair arrivals.

7.4 WIP credit must be horizon-aware

Replace:

immediate WIP credit = wip_qty * repair_yield

with:

expected good WIP arriving by deadline h
  = sum over WIP units P(returning good and arriving by h)

For an age-based WIP model, this can be estimated from remaining repair-time distributions. For a simpler aggregate model, use an expected completion curve by week.

The optimizer should rank an inventory increment using the change in a global, conserved objective.

Let x be all committed buffers and policy decisions. Define:

J(x) =
    sum_i service_weight_i * served_demand_i(x)
    - sum_k holding_cost_k(x)
    - sum_e transfer_cost_e(x)
    - sum_f repair_cost_f(x)
    - penalties_for_unmet_critical_demand(x)

For a candidate increment delta at SKU k:

MV(k) = [J(x + delta_k) - J(x)] / investment_cost(delta_k)

This has three important properties:

  • source inventory is charged once;
  • downstream and peer service improvements are included once in J;
  • repair, lateral, and hierarchical branches are evaluated together.

The existing target-group mechanism can still impose fill-rate targets and budgets. The global objective determines the marginal value among candidates; target groups remain constraints, not an excuse to multiply service benefits.

9. Two Implementation Tracks

9.1 Track A: conservative analytic release

This is appropriate if a full cross-location Monte Carlo engine is too expensive initially.

  1. Fix the hierarchical edge direction.
  2. Introduce explicit lateral edges with route lead time and capacity.
  3. Compute source free-stock distributions from current buffer, demand-over-lead-time distribution, reservations, and safety reserve.
  4. Use quantity-specific rescue probability or expected useful quantity.
  5. Use a source-conserving allocation policy.
  6. Treat repair arrivals as a separate, time-phased supply branch.
  7. Recompute the affected rescue neighbourhood after every committed inventory change.
  8. Recalculate all affected marginal values from the same deterministic state.

This track should initially restrict lateral rescue to one hop. One-hop rescue is much easier to validate and avoids accidental multi-hop double counting.

9.2 Track B: scenario-based reference engine

The long-term correctness reference should use common-random-number scenarios:

  1. Sample weekly demand for every relevant location and item.
  2. Sample own replenishment lead times.
  3. Sample lateral lane availability and transshipment times.
  4. Sample return events and repair turnaround.
  5. Process inventory, reservations, queues, and route capacities in chronological order.
  6. Apply the explicit allocation policy whenever several demands compete.
  7. Record served quantity, shortage quantity, source consumption, repair output, and arrival times.
  8. Evaluate the same scenarios before and after a candidate inventory increment.

Using the same random draws before and after the candidate makes the marginal comparison much less noisy and preserves deterministic replay when mc_seed is fixed.

The scenario engine should be the validation oracle for the analytic approximation. It does not need to replace the fast optimizer immediately.

10. Fixed-Point and Cycle Handling

10.1 Why two passes are not enough

In a mutual network:

L1 stock affects L2's service
L2 stock affects L1's service
both affect each other's free-stock probability
both affect the allocation state

A two-pass update may appear to converge on a small symmetric test while oscillating or becoming order-dependent on an asymmetric network. Monotonicity is not guaranteed once shared capacity, reservations, and allocation priority are introduced.

10.2 Safe options

Use one of the following explicit strategies:

  • one-hop lateral rescue in the first release;
  • a deterministic iterative solver with a convergence tolerance, maximum iterations, and failure status;
  • scenario simulation that processes the network in time order and does not need a static fixed point;
  • a constrained flow or stochastic-programming subproblem for each planning horizon.

If an iterative approximation is used, it must report:

  • iteration count;
  • maximum change in fill rate;
  • maximum change in rescue quantity;
  • whether convergence was reached;
  • whether damping was used;
  • which values were capped for numerical safety.

Never silently accept a non-converged rescue state as a valid MEIO result.

10.3 Separate graph cycles from time cycles

A cycle in the route graph is not necessarily a mathematical problem if every edge has positive time. It becomes a problem when the algorithm recursively credits the same stock through a cycle without advancing time.

For example:

L1 -> L2 -> L3 -> L1

must not allow one unit at L1 to create an infinite series of benefits. The first release should permit direct one-hop rescue only. Multi-hop rescue should use a time-expanded path and consume capacity and time at every hop.

11. Proposed Wire Model

The current site-ID list should be replaced or supplemented with explicit edge records. A conceptual Rust shape is:

struct SupplyEdge {
    source: SkuKey,
    destination: SkuKey,
    edge_kind: EdgeKind,
    lead_time: LeadTimeDistribution,
    capacity: CapacityDistribution,
    yield_rate: f64,
    route_reliability: f64,
    coverage_weight: f64,
    emergency_eligible: bool,
    priority: i32,
}

struct RepairProcess {
    input: SkuKey,
    facility: FacilityKey,
    output: SkuKey,
    return_lead_time: LeadTimeDistribution,
    shop_lead_time: LeadTimeDistribution,
    output_lead_time: LeadTimeDistribution,
    capacity: CapacityDistribution,
    yield_rate: f64,
    wip: WipDistribution,
}

The exact names can differ. The invariants cannot.

At minimum, the Rust wire format needs to distinguish:

  • replenishment dependants;
  • lateral rescue edges;
  • repair processes;
  • route timing and capacity;
  • source and destination item/site identity.

DependantChange also needs expansion if downstream service is updated through a candidate. A new shape should carry the service quantity or state change that was actually committed, not only a fill-rate number and a scalar wait time.

12. Interaction with Existing Features

12.1 Demand scope

Direct demand and indirect demand must remain explicit. A warehouse with zero direct customer demand may still have high internal replenishment demand and may be a valuable backup source. Conversely, a warehouse with indirect demand must not receive a full customer-service reward merely because its inventory is available to others.

Use separate metrics:

  • customer-facing service;
  • internal replenishment service;
  • lateral rescue contribution;
  • repair-pool contribution.

Then configure how those metrics roll into a target group.

12.2 BOM and kits

Lateral rescue of one component does not necessarily make a finished good serviceable if another component is short. The existing kit dependency logic should consume the service result of the component network after lateral and repair flows have been resolved.

Do not add a component rescue benefit directly to a parent assembly and also count the parent assembly's improved service independently unless the objective explicitly defines that hierarchy.

12.3 Zone pass

Use an explicit precedence rule:

  1. If a zone pass defines a pooled envelope, the envelope is a constraint or prior.
  2. Lateral rescue inside the zone may use the remaining free stock and explicit lanes.
  3. The zone pass's risk-pooling service gain is not added again as a peer-rescue gain.

The independent-variance assumption in the current zone pass is suitable as an MVP approximation, but it should not be described as proof that mutual rescue has been modelled.

12.4 Capacity and storage caps

A location storage cap limits inventory held at the site. A lateral route capacity limits movement between sites. A repair capacity limits transformation throughput. These are three different constraints.

The model should report all three separately:

inventory_capacity_exceeded
lateral_lane_capacity_exceeded
repair_capacity_exceeded

The current MEIO site-cap handling cannot replace edge and repair capacity modelling.

12.5 Planning date

Route eligibility, repair WIP age, effective capacity, and disruption windows are business data. They must be evaluated using the pipeline planning date, not the machine calendar date. This is especially important in replay and golden-fixture runs.

13. Foolproofing Invariants

The implementation should fail loudly when any of these invariants is violated.

Topology

  • Every edge has a valid source and destination SKU.
  • Reverse rescue is present only when explicitly configured.
  • A route kind is not inferred solely from a site hierarchy.
  • Forward and lateral edges are not silently merged.
  • Repair process edges cannot be traversed as ordinary replenishment edges.
  • Multi-hop rescue has a finite horizon and consumes time at every hop.

Quantity conservation

  • A source cannot allocate more good stock than its free quantity.
  • A route cannot move more than its capacity.
  • A repair facility cannot process more than its capacity.
  • Repaired good quantity is at most input quantity times yield.
  • Bad stock is never used as good stock.
  • One physical unit is not credited to two destinations simultaneously.
  • A firm reservation cannot also be counted as free rescue stock.

Probability and service

  • Fill rates, rescue probabilities, yields, and reliabilities remain in [0, 1].
  • Quantity-specific service cannot exceed requested demand.
  • Rescue availability is zero when free stock is zero, regardless of source fill rate.
  • A route with arrival after the demand deadline cannot serve that demand.
  • A non-converged fixed point is an explicit failure state, not a result.

Repair

  • Return rate is not allowed to create immediate supply.
  • WIP credit is based on expected arrival by horizon, not total WIP times yield.
  • Repair yield is applied exactly once.
  • Repair output allocation across sites is deterministic and source-conserving.
  • Multiple repair facilities are not collapsed into one facility without an explicit pooling rule.

Determinism

  • All peer and edge iteration uses canonical ordering.
  • Monte Carlo comparisons use common random numbers.
  • Fixed seeds produce identical snapshots.
  • Floating-point accumulation order is stable.
  • Diagnostics include the topology and policy version used for the result.

14. Validation Matrix

14.1 Unit tests

Test Expected result
no lateral edges baseline hierarchical result is unchanged
one forward edge source stock affects only its declared downstream destination
reverse route absent downstream stock does not rescue upstream node
one peer with zero free stock rescue quantity is zero even if peer FR is high
two claimants, one source capacity allocations sum to source capacity
request quantity doubled rescue probability or useful quantity changes
lateral route slower than forward route forward arrival wins for that shortage
repair yield zero no repair service arrives
repair lead time increased repair helps only later horizons; stock requirement rises where appropriate
WIP completion after horizon no immediate WIP credit
shared repair facility total repair output never exceeds facility capacity times yield
common disruption all affected lanes fail together in the disruption scenario
cycle L1-L2-L3 no infinite or repeated credit
zone plus lateral pooling benefit is not counted twice

14.2 Synthetic networks

Build small canonical fixtures:

  1. two-node hierarchy;
  2. two-node mutual backup with asymmetric transit times;
  3. four-node complete policy with unequal demand;
  4. hub-and-spoke with store-to-store lateral edges;
  5. central repair facility serving two stores;
  6. repair facility with capacity bottleneck;
  7. correlated demand with independent routes;
  8. independent demand with common-cause transport failure;
  9. BOM assembly whose component is laterally rescued;
  10. zone pass plus explicit lateral backup.

For every fixture, compare:

  • local fill rate;
  • group fill rate;
  • rescue quantity by edge;
  • source free stock before and after allocation;
  • repair input, output, and WIP;
  • arrival-time distribution;
  • total inventory cost;
  • objective value;
  • all capacity utilization.

14.3 Golden compatibility gate

With the new feature disabled, the existing golden fixture must remain byte-identical. With only the propagation-direction fix enabled, the expected fixture change must be isolated and explained. With lateral rescue enabled, every changed row should be attributable to a declared edge, policy, or repair-flow input.

Phase 0: repair the existing contract

  • Correct the transfer dependency orientation.
  • Add a warehouse-to-store regression fixture.
  • Do not add mutual rescue in the same change.
  • Run the golden comparison and document expected changes.

Phase 1: explicit lateral edges, one hop

  • Add route-level lateral edge records.
  • Add free-stock and lane-capacity calculations.
  • Use a deterministic allocation policy.
  • Evaluate quantity served by a peer, not FR * demand share.
  • Keep the feature disabled by default.

Phase 2: repair as timed supply

  • Add repair facility identity and route-level process edges.
  • Time-phase returns, WIP completion, repair yield, and output distribution.
  • Remove or isolate any duplicate net-demand credit.
  • Add repair lead-time regression tests.

Phase 3: scenario validation

  • Add common-random-number Monte Carlo across locations.
  • Include demand correlation, lead-time correlation, route disruptions, and shared capacities.
  • Compare analytic estimates to simulated service and calibrate conservative bounds.

Phase 4: controlled multi-hop and zone integration

  • Add time-expanded multi-hop rescue only after one-hop accounting is stable.
  • Define the relationship between zone envelopes and lateral rescue.
  • Add explicit double-counting checks.

16. Configuration Proposal

A future scenario configuration could be shaped as follows:

meio:
  network_service_model: hierarchical_plus_lateral
  lateral_rescue:
    enabled: false
    max_hops: 1
    allocation_policy: priority_then_proportional
    protect_source_quantile: 0.95
    use_route_capacity: true
    use_lane_reliability: true
    allow_cross_zone_rescue: false
    require_explicit_reverse_edge: true
    non_convergence_policy: fail
  repair:
    enabled: true
    model: time_phased_capacity_queue
    count_repair_as_net_demand_reduction: false
    allow_repaired_output_pooling: true
    use_wip_age_distribution: true
  validation:
    max_rescue_iterations: 20
    rescue_convergence_tolerance: 0.000001
    common_random_numbers: true

The exact configuration names are illustrative. The important design point is that topology, allocation policy, repair semantics, and approximation mode are explicit and versioned.

17. Final Recommendation

Implement the idea, but do not implement the proposal as a direct substitution of:

(1 - FR) * LT

with:

(1 - FR) * [(1 - r) * LT + r * tau]

Instead:

  1. Fix the current hierarchical edge direction.
  2. Introduce typed, directed route edges.
  3. Compute peer rescue from free, allocatable stock and route capacity.
  4. Model lateral, forward, emergency, and repair branches as time-dependent competing supply.
  5. Treat repair as a delayed transformation with yield, WIP, capacity, and output allocation.
  6. Use a global conserved service objective for marginal-value ranking.
  7. Start with one-hop lateral rescue and an explicit allocation policy.
  8. Use a deterministic scenario simulator as the correctness oracle before adding multi-hop fixed-point logic.
  9. Prevent zone pooling and lateral rescue from claiming the same benefit twice.
  10. Fail loudly when topology, capacity, convergence, or conservation assumptions are invalid.

The concise rule is:

A location can back up another location only to the extent that it has allocatable good stock, an eligible route, available capacity, a policy that awards the stock, and an arrival time that beats the alternatives. Repair is another delayed supply process, not free stock.

That rule supports hierarchical replenishment, mutual backup, and repair-capable locations without forcing all three behaviours into one scalar wait-time formula.

Appendix: Formula Cheat Sheet

Source free stock

free_stock_j(t) = max(
    0,
    good_on_hand_j(t)
    - own_protection_j(t)
    - firm_reservations_j(t)
    - already_allocated_outbound_j(t)
)

Quantity-specific peer rescue

useful_qty_ji(q, h) = E[
    min(q, free_stock_j)
    * lane_available_ji
    * route_arrives_by_h_ji
    * allocation_awards_ji
]

Independent approximation only

r_i(q, h) = 1 - product_j(1 - P(rescue_ji(q, h)))

Timed competing supply

arrival_i = min(
    lateral_arrival_i,
    forward_arrival_i,
    repair_arrival_i,
    emergency_arrival_i
)

Repair output

repair_good_output(t)
  = min(bad_input_to_queue(t), repair_capacity(t)) * repair_yield(t)

Marginal value

MV(k) = [global_service_value(x + delta_k) - global_service_value(x)]
        / inventory_investment(delta_k)

Conservation checks

sum(dest_allocations_from_j) <= free_stock_j
sum(flow_through_repair_facility) <= repair_capacity
good_repair_output <= repair_input * yield
served_demand_i <= demand_i