How a likely cause gets suggested
A number on its own — minus 48 bolts — tells a manager what changed but not why, and the why is what decides what to do about it. Shrinkage means a security conversation. A miscount means recount before you touch anything. An unrecorded sale means a till or process problem. A receiving error means a word with the supplier. This post is about how the system turns a bare variance into a likely cause: the small bundle of evidence it assembles for each gap, the single Bedrock Haiku 4.5 call that picks one cause from a fixed list of four and explains itself in a sentence, and the hard line that keeps the model labelling numbers rather than moving stock.
Key takeaways
- Only material variances reach the model. Lines in agreement and minor variances never cost a call.
- For each gap the system builds a small evidence bundle: the gap, recent sales, last receiving, last count date.
- One Bedrock Haiku 4.5 call returns a cause from a fixed list of four, with a one-line rationale.
- The four causes are shrinkage, miscount, unrecorded sale, and receiving error — and nothing else.
- The model labels a number the code already computed. It cannot move stock, change a gap, or invent a SKU.
A number needs a why
Part 3 left us with a proven, sized gap: BOLT-M6-50, short 48 units, −10%, −£16.80. That tells a manager what changed, but the action depends entirely on why. Shrinkage points to a security or process conversation. A miscount means recount before touching anything. An unrecorded sale points at a till or a workflow that isn’t capturing every transaction. A receiving error means a word with the supplier and a check of the delivery paperwork. Same −48 on the screen; four completely different next steps. The job of this piece is to put the most likely why next to the number, so the manager starts from a hypothesis rather than a blank page.
The evidence bundle
The model doesn’t get the whole database, and it doesn’t get a vague prompt. For each material variance the engine assembles a small, structured evidence bundle — the handful of facts that actually discriminate between the four causes — all pulled with plain queries before any call is made:
- The gap itself. Direction and size in units, percent, and value. A shortage and an overage point at different causes; a 10% shortage and a 200% overage point at different causes again.
- Recent sales for the SKU. Units sold since the last count. A shortage that matches a sales spike is unremarkable; a shortage with flat sales is not.
- The last receiving. When stock was last booked in against this SKU, and how much. A gap right after a delivery smells like a receiving error; a gap with no recent delivery does not.
- The last count date. How long the gap had to accumulate. Forty-eight units lost over eight weeks reads differently from 48 lost since Tuesday.
Keeping the bundle small is deliberate: it makes the call cheap, keeps the model focused on the signals that matter, and means the same gap with the same evidence gets a stable answer. The model is reasoning over four or five numbers and dates, not trawling a warehouse.
One call, four causes
The bundle goes to a single Bedrock Haiku 4.5 call — the stkr-cause Lambda — via Global cross-Region inference. The prompt is tight: here is the gap, here is the evidence, pick exactly one cause from this list and give one sentence of reasoning. The list is closed:
- Shrinkage. Stock that left without being recorded — theft, breakage, spoilage. The signature is a sustained shortage with normal sales and no recent receiving, which is exactly our bolt line.
- Miscount. The count itself is wrong — a section skipped, a pack size misread, a digit transposed. The signature is often a gap that’s suspiciously round or suspiciously large, or an overage that has no source.
- Unrecorded sale. The item left as a legitimate sale that never made it into the system. The signature is a shortage that lines up with foot traffic or a known till issue, with no matching record.
- Receiving error. The wrong quantity was booked in — a delivery short-shipped, double-counted at the dock, or keyed against the wrong SKU. The signature is a gap that appears right after a recent receiving.
For BOLT-M6-50, the evidence — a steady 10% shortfall, no delivery in 30 days, flat sales, eight weeks since the last count — points away from receiving (no recent delivery) and away from a one-off sale spike, and toward slow, unrecorded loss. The call returns shrinkage with the rationale: “a sustained shortage with no recent receiving and normal sales points to loss rather than a recording error.” That label and that sentence ride along with the variance into the approval queue.
The guardrails on the model
This is the only place a model touches the system, so the rails are explicit:
- It picks from a fixed list. The four causes are the entire vocabulary. A reply that isn’t one of them is rejected by the code that reads it.
- It returns a label and a sentence, never an action. The response schema is a cause plus a rationale — there is no field in which the model could say “post −48” even if it wanted to. The adjustment is computed by code in Part 3 and posted by a human in Part 5.
- It fails safe. If Bedrock errors, times out, or returns something off-list or unparseable, the variance doesn’t vanish — it’s queued anyway, labelled cause unknown, so a manager still sees the gap. A missing suggestion is a smaller problem than a hidden variance.
- It never reads or writes stock. The
stkr-causeLambda’s IAM role can call Bedrock and read the evidence it was handed. It has no permission to writestkr-inventory. The model literally cannot move stock.
This is the same division of labour as the bill matcher: code owns every number and every decision that touches the books; the model is allowed to be helpful exactly where being occasionally wrong is cheap — a suggestion a human reads before acting. A wrong cause label costs a manager a moment’s thought. A wrong number would cost real money.
Why this shape
- Only material variances are sent, so the model never runs on lines that don’t matter — and the cost tracks variances, not SKUs.
- A small, structured evidence bundle keeps the call cheap, focused, and stable across re-runs.
- A closed list of four causes makes the output checkable — off-list replies are rejected, not displayed.
- The response is a label and a sentence, with no field that could express an action.
- Fail-safe: a model failure downgrades to “cause unknown” and still queues. The gap is never lost.
- The cause Lambda can’t write inventory. The guardrail is enforced by IAM, not just intention.
The gap is sized and the cause is suggested. The last piece puts both in front of a person: how an adjustment gets approved, and how an approved one is written back to inventory with a trail behind it.
All posts