Inventory
You are setting stock policy: how much to hold, when to reorder, how much to reorder. You are not buying it (that is procurement) and not moving it (that is logistics-ops). You decide the numbers that tell the buyer what to order and when.
Leave behind two checkable artifacts every time:
- Reorder-policy table — one row per SKU:
sku, abc_class, avg_demand, lead_time, safety_stock, reorder_point, order_qty, review_mode. This is the standing policy. - Replenishment trigger list — the subset of SKUs whose
on_hand <= reorder_pointright now, with a suggested order quantity. This is the action.
scripts/verify.sh checks both for shape and the structural invariants (ROP ≥ safety stock, order_qty > 0, no false triggers). Run it before you hand anything off. It validates structure, not whether the buffers are commercially smart — that judgment is yours.
Pick the job
The flow genuinely branches. Find the row that matches the ask before doing anything else.
| Ask sounds like | Job | You produce |
|---|---|---|
| "which SKUs deserve tight control", "run ABC" | Classify | ABC×XYZ class per SKU → the abc_class column + a policy posture per class |
| "how much safety stock", "we keep running out / sitting on dead stock" | Size the buffer | safety_stock per SKU, chosen Z, chosen formula |
| "what's my reorder point", "set up min/max", "weekly or continuous" | Set policy | reorder_point, order_qty, review_mode → the full policy table |
| "is our stock policy working", "turnover", "cut dead stock" | Track | KPI panel against the business's own history + cycle-count cadence |
Classify before you size, size before you set policy. A uniform policy across every SKU is the original sin (anti-patterns table).
Classify first: ABC × XYZ
Rule: never apply one uniform service level or review mode to every SKU — control is a budget, spend it where the value and the volatility are. Bad: 95% service on all 800 SKUs. Good: 99% on the high-value/stable AX items, make-to-order or near-zero buffer on the low-value/erratic CZ items.
- ABC = Pareto split by annual consumption value (
annual_demand × unit_cost), sorted descending and cumulated. A ≈ top ~80% of value (usually ~20% of SKUs), B the next band, C the long low-value tail. - XYZ = split by demand variability via coefficient of variation
CV = σ_demand / mean_demand. X = stable (low CV), Y = fluctuating, Z = erratic.
The 9-cell matrix maps to policy: AX → tight automated min/max, highest service; CZ → manual, minimal buffer or make-to-order; AZ → high value but erratic, the candidate for DDMRP (below). Full cumulative-value worked sort, CV cut points, and the 9-cell→(review mode, service level, buffer posture) matrix are in references/abc-xyz.md.
Safety stock
Rule: match the formula to which thing actually varies. Picking the demand-only formula when the supplier's lead time swings is the most common under-buffering mistake — you buffer the wrong variance and still stock out.
demand varies, lead time stable: SS = Z · σ_demand · √LT
both vary, independent: SS = Z · √(LT · σ_demand² + avg_demand² · σ_LT²)
both vary, correlated (King): SS = Z · σ_demand · √LT + Z · avg_demand · σ_LT
Z is set by your target service level, never picked by feel:
| Service level | Z |
|---|---|
| 90% | 1.28 |
| 95% | 1.65 |
| 97.5% | 1.96 |
| 99% | 2.33 |
Higher service costs disproportionately more buffer — the tail is fat, so each extra point past ~98% buys a chunk of cash you reserve for A/critical items and deny to C items. Bad→Good: supplier "usually 5 days but sometimes 12" + you used Z·σ_demand·√LT → switch to the independent or King formula so σ_LT is in the buffer. How to estimate σ_demand and σ_LT from sales/receipt history, all three formulas worked with numbers, and the diminishing-returns curve are in references/safety-stock.md.
Reorder point and order quantity
These are two separate decisions — conflating them is the classic beginner error. ROP answers when, order quantity answers how much.
ROP = (avg daily demand × lead-time days) + safety_stock ← when to trigger
EOQ = √(2 · D · S / H) ← how much to order
D = annual demand, S = cost per order, H = annual holding cost per unit
EOQ minimizes ordering + holding cost under stable demand. It is an order-size lever and never a trigger — you do not "wait until you can order an EOQ." Keep reorder_point and order_qty as separate columns.
Continuous vs periodic review is a real branch:
| Mode | How it works | Use when | Cost |
|---|---|---|---|
| Continuous (min-max / s,S) | Order the moment on-hand hits ROP s, bring up to S |
A items, perpetual stock visibility | Fastest reaction, needs live counts |
| Periodic | Check on a fixed cadence (e.g. every Friday), order up to target | B/C items, batched POs, no live system | Needs a bigger buffer to cover the extra review-interval exposure |
Periodic review adds Z · σ_demand · √(review_interval) of exposure on top of lead-time exposure — budget for it. Worked ROP/EOQ examples, the review-interval buffer adjustment, and the exact policy-table + trigger-list schemas (the columns verify.sh checks) are in references/reorder-policies.md.
Demand-driven option (DDMRP)
When a static ROP keeps whipsawing on erratic demand — you re-tune it monthly and it is still wrong — switch that SKU to buffer zones instead of a fixed point.
- Red = safety/critical, sized off lead-time and demand variability.
- Yellow = demand coverage over the lead time.
- Green = order cycle / order frequency.
Trigger fires when net flow position = on_hand + on_order − qualified_demand drops from green into yellow. The zones recalculate as demand ramps, so you are not hand-editing safety stock every cycle.
Decision rule: reach for DDMRP only on erratic (Z-class), long-lead, or strategic SKUs — never for steady C items where a flat ROP is cheaper to run. Zone sizing and the net-flow trigger are in references/ddmrp.md. Set review_mode = ddmrp for these SKUs.
Track it
Prove the policy works with a KPI panel — and recompute every metric against the business's own history; the benchmarks below are sanity rails to flag against, not numbers to hallucinate or paste as targets.
| KPI | Formula | Rough rail |
|---|---|---|
| Inventory turnover | COGS / avg inventory | ≈6–8×/yr (consumer goods) |
| Days of inventory on hand | avg inventory / COGS × 365 | ≈30–60 days |
| GMROI | gross margin / avg inventory cost | target > 2.5 |
| Sell-through | units sold / units received | ≈70–85% / mo |
| Stockout rate | stockout events / order lines | target < 5% |
| Fill rate | lines filled complete / total lines | target > 95% |
| Dead-stock flag | SKUs with zero movement over a defined window | flag, don't reorder |
Cycle counting, not an annual full count. Count in a rolling sequence frequency-tiered off ABC: A items often (monthly/quarterly), C items rarely (annually). The warehouse never stops, and errors on high-value items surface fastest.
Anti-patterns
| Anti-pattern | Why it bites | Do instead |
|---|---|---|
| One uniform service level / review mode across all SKUs | Overspends on C, under-protects A | ABC×XYZ → differentiated Z and review mode |
| Demand-only SS when lead time is volatile | Buffers the wrong variance, still stocks out | Independent or King formula so σ_LT is in the buffer |
| Treating ROP and order quantity as one number | "Wait to order a full batch" → late triggers | Separate reorder_point (when) and order_qty (how much) columns |
| Using EOQ as a reorder trigger | EOQ is a size lever, not a signal | Trigger on ROP; size with EOQ |
| Picking Z by feel | Service level is undefined and indefensible | Set service level → read Z from the table |
| Static safety stock on erratic demand | Re-tuned monthly and still wrong | DDMRP buffer zones, review_mode = ddmrp |
| Annual full physical count | Warehouse stops; high-value errors found too late | ABC-tiered rolling cycle counts |
| Placing/negotiating the PO here | Wrong skill owns the buy | Hand the trigger list to procurement |
Hand-offs
- Raising/negotiating the purchase order, supplier choice, payment terms, PO approval, three-way match →
procurement(see../procurement/SKILL.md). You hand it the trigger list; it executes the buy. - Warehousing, picking, packing, shipping, carriers, returns flow → logistics-ops. You set the on-hand target; it physically fulfills.
- Statistical/ML demand forecast, seasonality decomposition → forecasting. You consume a demand estimate and its variability; you do not own the model.
- Live KPI dashboard → dashboard. Recurring KPI report → reporting.
- Pivot tables, ABC cumulative-value sort mechanics in a sheet → spreadsheet-ops.