SKILL: Northwind Components ERP Decision Tasks
Reusable workflow for the Northwind Components inventory / order-fulfillment
domain. These tasks give you a memo + an answer_template.json, and ask you to
query the shared ERP API, apply business rules, and emit one JSON object that
matches the template exactly. Rules below were verified/refined against task
feedback during training.
1. ENVIRONMENT / API USAGE
Base URL is provided by the runner (a remote host). Ignore any prompt text that
tells you to "start a local environment" or use 127.0.0.1:8007 or read an
env/ directory — always use the runner-provided base URL and the public HTTP
API only. Never read env source, data files, or gold answers.
GET endpoints (all return JSON):
/health — manifest with record counts (sanity check; confirms dataset).
/products , /products/<sku> — product master.
/customers , /customers/<customer_id>.
/suppliers — list (no per-id route needed; small set).
/warehouses — id, name, region, zip. Three warehouses:
WH_NORTH (07102), WH_CENTRAL (60607), WH_WEST (89502).
/inventory?warehouse_id=&sku= — returns a LIST (filter by either/both).
Omit warehouse_id to get one row per warehouse for a SKU (needed for
transfer sourcing).
/purchase_orders?supplier_id=&sku=&status=.
/orders?wave=&required_date=&customer_id= , /orders/<order_id>.
/shipping/quote?warehouse_id=&destination_zip=&weight_lb=&speed=
(speed in ground|two_day|overnight).
/incidents?start=&end=&supplier_id=&sku=&incident_type=&status=
— the date window applies to open_date (inclusive on both ends).
/boms , /boms/<bom_id>.
Practical fetching notes:
- Use
curl (reliable, ~1s/call). If you script in Python, set an explicit
per-request timeout — bare urllib with no timeout can hang indefinitely.
- Cache per-URL results in memory; many tasks request the same product /
inventory repeatedly.
- An inactive or missing SKU's
/products/<sku> may 404 — handle gracefully.
- The shipping quote endpoint COMPUTES
total_cost from the weight_lb you
pass. total_cost ≈ base_rate * (1 + fuel_surcharge_rate), and base_rate
scales (almost) linearly with weight. So the weight you send is load-bearing —
send the exact intended weight at full precision, then round the returned
total_cost to 2 decimals. zone_distance and service_days come straight
from the response (do not recompute).
Record shapes you will use constantly:
- product:
{sku, name, active(bool), category, supplier_id, unit_cost, weight_lb, safety_stock, overstock_threshold}.
- inventory row:
{warehouse_id, sku, on_hand, reserved, quarantined, last_count_date}.
- customer:
{customer_id, name, tier, margin_band, account_status, risk_flag}.
account_status in {active, review_required, blocked}.
risk_flag in {none, credit_watch, fraud_watch}.
- supplier:
{supplier_id, name, region, quality_status} with
quality_status in {approved, watch, quality_hold}.
- order:
{order_id, wave, customer_id, warehouse_id, destination_zip, priority, required_date, shipping_speed, lines:[{line_id, sku, quantity, unit_price}]}.
- PO:
{po_id, sku, supplier_id, warehouse_id, quantity, status, eta} with
status in {open, confirmed, received, cancelled}.
- incident:
{incident_id, supplier_id, sku, warehouse_id, incident_type (RMA|WORK_ORDER), severity (low|medium|high|critical), status (open|closed), open_date, close_date, resolution_cost, root_cause}.
- BOM:
{bom_id, name, warehouse_id, target_date, components:[{sku, quantity_per_kit}]}.
2. THE CORE INVENTORY RULE (USED IN MULTIPLE TASKS)
Effective (freely usable) available stock at a warehouse:
effective_available = on_hand - reserved - quarantined - safety_stock
Reserved, quarantined, AND the safety-stock buffer ("normal operating buffer")
are all PROTECTED and must NOT be counted as available for new demand. This
4-term formula was verified correct (it produced perfect scores on the
allocation and replenishment tasks).
Critical detail: DO NOT clamp effective_available to zero. Keep it raw,
including negative values (e.g. on_hand 1, reserved 0, quarantined 0, safety 52 → -51). The reported "effective available" field and any gap math
(gap = required - effective_available) depend on the negative value. Clamping
to 0 understated gaps and cost score.
Caveat for the "expedite queue" style task (Section 6): that task has a separate
low_stock vs shortage distinction and a standalone safety_stock concept,
so there shortage is judged on on_hand - reserved - quarantined versus the
order quantity; treat safety stock only as the low-stock threshold there. Use
the full 4-term protected formula for ALLOCATION and REPLENISHMENT tasks.
For transfer sourcing, a donor warehouse's lendable quantity is its own
effective_available (same 4-term formula) — never lend protected stock.
3. CUSTOMER / ACCOUNT GATING (release decisions)
Precedence for customer-driven holds (highest first):
account_status == blocked → account-level block.
risk_flag == fraud_watch → account-level block.
risk_flag == credit_watch → credit hold (account-level).
account_status == review_required → account-level review.
- otherwise → none.
Account/risk holds (blocked, fraud_watch, review_required, and credit_watch)
stop the order at the ACCOUNT level → these orders go on the "blocked orders"
list and their lines become manual_review.
Product-master problems are LINE-ONLY, NOT account blocks:
product.active == false → that line is manual_review with reason
inactive_product, but the ORDER is NOT added to blocked_orders (unless it
also has an account/risk hold). An order with one inactive line and other
normal lines is mixed_actions, not blocked.
4. ALLOCATION / TRANSFER TASK SOP (verified 1.0)
Goal: classify every order line in a wave as ship | transfer | backorder | manual_review, emit transfer requests, blocked orders, per-order rollup, and a
summary.
Per line:
- Resolve customer gating (Section 3). If an account/risk hold applies →
action = manual_review, ship_quantity = 0, transfer_quantity = 0,
backorder_quantity = 0, primary_reason = the matching reason
(account_blocked | fraud_watch | account_review_required). Add the order to
blocked_orders.
- Else if product inactive →
manual_review, reason inactive_product
(line only; order NOT blocked).
- Else compute
req_ea = effective_available(requested_warehouse) (raw, may be
negative):
req_ea >= qty → ship, ship_quantity = qty, reason none.
- else
ship_part = max(req_ea, 0), remaining = qty - ship_part:
- Find ONE other warehouse whose own effective_available
>= remaining
(prefer the largest such donor; tie-break by warehouse_id ascending).
If found → transfer: ship_quantity = ship_part,
transfer_from = donor, transfer_quantity = remaining,
reason insufficient_effective_stock. Emit a transfer_request row.
- else →
backorder: ship_quantity = ship_part,
backorder_quantity = remaining, reason insufficient_effective_stock.
- Choose a SINGLE source warehouse for a transfer line; leave any usable
requested-warehouse quantity as
ship_quantity.
primary_reason enum: none | account_blocked | account_review_required | fraud_watch | inactive_product | insufficient_effective_stock.
Order rollup outcome enum: ready_to_ship | needs_transfer | has_backorder | manual_review | mixed_actions:
- all lines ship →
ready_to_ship.
- all lines manual_review →
manual_review.
- any manual_review mixed with other actions →
mixed_actions.
- only ship/transfer (≥1 transfer) →
needs_transfer.
- any backorder (no manual_review) →
has_backorder.
Summary integer keys: total_orders, total_lines, ship_lines, transfer_lines,
backorder_lines, manual_review_lines, blocked_orders (count), transfer_units,
backorder_units.
Ordering: line_actions and transfer_requests sorted by order_id asc then
line_id asc; blocked_orders and order_rollup sorted by order_id asc.
5. KIT REPLENISHMENT TASK SOP (verified ~0.83)
Goal: for BOM builds at a planning warehouse, produce per-component coverage,
transfer requests, purchase requisitions, exclusions, and totals.
total_required[sku] = Σ over builds (build_quantity * quantity_per_kit).
A SKU appearing in multiple BOMs sums across them.
target_effective_available = effective_available at the planning warehouse
(4-term formula, RAW / may be negative). This is the reported field.
gap = total_required - target_effective_available.
timely_po_qty = sum of quantities of purchase orders that are
same planning warehouse, status open or confirmed, AND
eta <= needed_by (the component's earliest required build date). POs that
are received, cancelled, at another warehouse, or arriving after the
build date are NOT timely. coverage_po_ids = sorted ids of those timely POs.
- Decision cascade per component (
final_action / exclusion_reason):
gap <= 0 and target_effective_available >= overstock_threshold
→ overstock_excluded / target_overstock.
gap <= 0 (otherwise) → no_action_stocked / stocked_no_gap.
target_effective_available >= overstock_threshold (gap>0)
→ overstock_excluded / target_overstock (do not add to an
already-overstocked SKU).
timely_po_qty >= gap → timely_po_covered / timely_po_covers_gap.
- else cover
gap - timely_po_qty first by TRANSFERS from other warehouses'
effective_available (largest donor first, tie by warehouse_id asc):
- fully covered by transfers →
transfer_only / reason none.
- residual remains →
purchase_required / reason none, and create a
purchase requisition for the residual quantity.
- Purchase requisition:
supplier_id from product master, warehouse_id =
planning warehouse, quantity = residual, needed_by = component build date,
unit_cost = product.unit_cost (2 dp), extended_cost = unit_cost*quantity
(2 dp).
excluded_components list contains every component whose exclusion_reason
is not none (target_overstock | timely_po_covers_gap | stocked_no_gap),
with supporting_po_ids = the timely PO ids for timely_po_covers_gap,
else [].
Summary:
component_count = number of components.
total_purchase_units = Σ requisition quantities.
total_purchase_cost = Σ extended_cost (2 dp).
total_transfer_units = Σ transfer quantities.
timely_po_covered_units = Σ of the gap covered by timely POs for
timely_po_covered components — i.e. the gap amount, NOT the full PO quantity.
(Reporting full PO qty here cost score; report min(gap, timely_po_qty) = the
gap.)
Ordering: component_plan, purchase_requisitions, excluded_components
sorted by sku asc. transfer_requests sorted by sku asc, then quantity desc,
then from_warehouse_id asc. kit_targets sorted by bom_id asc.
Open question (lower confidence): when a component has no gap AND is at/above
overstock_threshold, whether the label should be overstock_excluded vs
no_action_stocked — prefer overstock_excluded when
effective_available >= overstock_threshold since that condition is literally
true.
6. EXPEDITE-QUEUE / DISPATCH-CONTROL TASK SOP (partial)
Goal: per order, report inventory_status, customer_exception,
final_decision, next_action, the three SKU exception lists, and a shipping
quote; plus a wave summary.
Per-line inventory classification (this task tracks safety stock separately):
available = on_hand - reserved - quarantined (do NOT subtract safety here).
inactive line: product.active == false.
shortage line: available < quantity.
low_stock line: can fulfill but the buffer is breached (available below /
dropping below safety_stock).
Order-level inventory_status enum (priority high→low):
inactive_and_shortage (has both) > inactive_sku > shortage >
low_stock > ready.
customer_exception enum mirrors Section 3:
account_blocked | fraud_watch | credit_watch | review_required | none.
final_decision enum {ship_now, delayed_release, manual_review, backorder,
reject_hold} paired with next_action {release_to_pick, delay_and_monitor,
send_account_review, create_backorder, hold_credit_or_fraud,
escalate_product_master}. Natural pairings:
- account_blocked/fraud_watch/credit_watch → reject_hold / hold_credit_or_fraud.
- review_required → manual_review / send_account_review.
- inactive SKU → manual_review / escalate_product_master.
- shortage → backorder / create_backorder.
- low_stock → delayed_release / delay_and_monitor.
- clean & ready → ship_now / release_to_pick.
Shipping quote: speed = the ORDER's shipping_speed (memo may explicitly say
"use the order's requested shipping speed"; quotes are required even when the
queue decision is not "release"). weight = Σ(product.weight_lb * quantity) over
all lines. Report zone_distance, service_days from the response and
total_cost_usd = round(total_cost, 2).
Summary: order_count, decision_counts (one integer per decision enum value),
total_shipping_cost_usd (2 dp), and id lists blocked_order_ids (reject_hold),
manual_review_order_ids, backorder_order_ids, inactive_sku_order_ids — all
sorted ascending. Records sorted by order_id asc; SKU lists sorted asc.
CAUTION (unresolved): this task scored low in training and was insensitive to
decision-precedence changes, meaning the dominant error was in the per-line
classification and/or shipping-cost basis rather than the decision mapping.
When you face this task, treat the available-stock formula and the shipping
WEIGHT basis as the highest-risk fields: double-check whether quarantine should
be subtracted and whether weight is the full Σ(weight_lb*qty). The precedence
ordering above is plausible but the score was driven by the numeric fields.
7. SUPPLIER INCIDENT SCORECARD SOP (verified 1.0)
Filter incidents by open_date within the requested window (the /incidents
start/end params do this, inclusive both ends). Group by supplier.
Per supplier row:
incident_count.
incident_percentage = count / (total filtered incidents) * 100, rounded to
the requested precision (commonly 1 decimal). Denominator = the WHOLE filtered
population, not per-type.
total_resolution_cost = Σ resolution_cost (2 dp).
avg_duration_days (2 dp). Duration per incident:
- closed:
close_date - open_date in calendar days.
- open:
analysis_date - open_date in calendar days.
rma_count (incident_type RMA), work_order_count (WORK_ORDER).
open_incident_count (status open).
severe_incident_count = severity in {high, critical}.
recommendation_code via the task's recommendation policy. Apply codes in the
stated PRECEDENCE order (first match wins), e.g. a typical policy:
ESCALATE_SUPPLIER: quality_hold with ≥3 filtered incidents, OR any
critical RMA (incident_type RMA and severity critical), OR ≥3 RMAs with
≥ a cost threshold.
PROCESS_REVIEW: WORK_ORDER count ≥3 AND greater than RMA count.
WATCHLIST: quality_status in {watch, quality_hold}, OR incident_count ≥4,
OR total cost ≥ a threshold, OR severe_incident_count ≥2.
MONITOR: none of the above.
Always read the exact thresholds and precedence from the task payload — they
are spelled out and must be applied literally.
Derived outputs:
top_escalation_suppliers = supplier_ids whose code is ESCALATE_SUPPLIER,
ordered by incident_count desc, then total_resolution_cost desc, then
supplier_id asc.
highest_cost_supplier_id = supplier with max total_resolution_cost
(tie-break supplier_id asc).
highest_share_supplier_id = supplier with max incident_count
(tie-break supplier_id asc).
- summary: filtered_incident_count, supplier_count (suppliers with ≥1 incident),
total_resolution_cost (2 dp), overall_rma_count, overall_work_order_count.
Rows sorted by supplier_id asc.
8. SUPPLIER REPLENISHMENT-CONTROL SOP (partial)
For each target supplier over an analysis window, compute the same incident
stats as Section 7 (filtered on open_date in the window):
recent_incident_count, recent_rma_count, severe_or_critical_count
(high|critical), open_incident_count, affected_skus (sorted unique),
sample_incident_ids (sorted, max 5). Compute these EXACTLY — they are the bulk
of the score and must match the raw filtered population precisely. Verify your
window endpoints and that you filter on open_date.
held_po_ids per supplier = that supplier's open/confirmed POs that are
held because of the control decision. Top-level held_po_ids = sorted union;
release_supplier_ids = suppliers whose decision is monitor_only.
Decision enum {freeze_new_replenishment, buyer_review_required, monitor_only}.
Lower-confidence policy (no fully-specified rule was given in training; reason
from quality_status + active/open risk):
freeze_new_replenishment: supplier on quality_hold (strongest control).
buyer_review_required: watch status, or unresolved/open incidents, or a
critical incident on an otherwise-OK supplier.
monitor_only: approved with no active risk.
Held POs follow the decision: freeze and buyer_review suppliers' open/confirmed
POs are held; monitor_only suppliers are released. If a task payload spells out a
decision policy, apply it literally instead of the heuristic above.
Summary: suppliers_reviewed, freeze_count, buyer_review_count, monitor_count,
held_po_count, total_recent_incidents.
9. OUTPUT / FORMATTING CONVENTIONS (ALL TASKS)
- Return ONLY the JSON object that matches
answer_template.json. No prose.
- Include EVERY
required_top_level_key and every item_required_keys field,
even when empty ([], 0, "none", or null per the template's enum).
- Respect each field's declared type: integer fields must be ints (not floats),
enum fields must use an allowed value verbatim, currency fields are numbers
rounded to 2 decimals.
- Set required constant fields exactly (e.g.
wave_id, task_id,
required_value).
- Apply the template's declared sort order to every list (commonly ascending by
id/sku; some lists have multi-key orders — follow them precisely).
- SKU/id sub-lists are sorted ascending and de-duplicated.
effective_available-style fields are reported RAW (can be negative); do not
clamp unless the template says otherwise.
- Currency rounding is the LAST step (round each line, and round summary totals
from the rounded/exact components consistently to 2 dp).
- Quote
total_cost is rounded to 2 dp; zone_distance and service_days come
from the quote response unchanged.
10. COMMON MISJUDGMENTS THAT COST SCORE
- Clamping effective availability to 0 instead of keeping negative values
(breaks gap math and the reported field).
- Forgetting to subtract
safety_stock (the "normal operating buffer") from
available stock in allocation/replenishment tasks.
- Counting
received/cancelled POs, other-warehouse POs, or late-ETA POs as
"timely" coverage. Timely = same warehouse, open/confirmed, eta ≤ needed_by.
- Reporting full PO quantity for "covered units" instead of the gap actually
covered.
- Treating an inactive-product line as an account block (it is line-only;
it does NOT put the order on the blocked list).
- Splitting a transfer across multiple source warehouses when the task wants a
single source per line.
- Wrong incident date semantics: the window applies to
open_date, inclusive;
open incidents' duration runs to the analysis_date, closed incidents'
duration runs to close_date.
- Misordering recommendation precedence (first matching tier wins) or using the
wrong percentage denominator (use the whole filtered population).
- Sending the wrong weight or speed to the shipping quote (use the order's
shipping_speed and the full Σ(weight_lb*qty)); recomputing zone/service_days
instead of taking them from the response.
- Emitting floats where integers are required, or skipping required keys with
empty values.
1---2name: reflect-3-attempt-03-283description: SKILL: Northwind Components ERP Decision Tasks4---5# SKILL: Northwind Components ERP Decision Tasks67Reusable workflow for the Northwind Components inventory / order-fulfillment8domain. These tasks give you a memo + an `answer_template.json`, and ask you to9query the shared ERP API, apply business rules, and emit one JSON object that10matches the template exactly. Rules below were verified/refined against task11feedback during training.1213---1415## 1. ENVIRONMENT / API USAGE1617Base URL is provided by the runner (a remote host). Ignore any prompt text that18tells you to "start a local environment" or use `127.0.0.1:8007` or read an19`env/` directory — always use the runner-provided base URL and the public HTTP20API only. Never read env source, data files, or gold answers.2122GET endpoints (all return JSON):23- `/health` — manifest with record counts (sanity check; confirms dataset).24- `/products` , `/products/<sku>` — product master.25- `/customers` , `/customers/<customer_id>`.26- `/suppliers` — list (no per-id route needed; small set).27- `/warehouses` — id, name, region, zip. Three warehouses:28 WH_NORTH (07102), WH_CENTRAL (60607), WH_WEST (89502).29- `/inventory?warehouse_id=&sku=` — returns a LIST (filter by either/both).30 Omit `warehouse_id` to get one row per warehouse for a SKU (needed for31 transfer sourcing).32- `/purchase_orders?supplier_id=&sku=&status=`.33- `/orders?wave=&required_date=&customer_id=` , `/orders/<order_id>`.34- `/shipping/quote?warehouse_id=&destination_zip=&weight_lb=&speed=`35 (speed in `ground|two_day|overnight`).36- `/incidents?start=&end=&supplier_id=&sku=&incident_type=&status=`37 — the date window applies to `open_date` (inclusive on both ends).38- `/boms` , `/boms/<bom_id>`.3940Practical fetching notes:41- Use `curl` (reliable, ~1s/call). If you script in Python, set an explicit42 per-request timeout — bare `urllib` with no timeout can hang indefinitely.43- Cache per-URL results in memory; many tasks request the same product /44 inventory repeatedly.45- An inactive or missing SKU's `/products/<sku>` may 404 — handle gracefully.46- The shipping quote endpoint COMPUTES `total_cost` from the `weight_lb` you47 pass. `total_cost ≈ base_rate * (1 + fuel_surcharge_rate)`, and `base_rate`48 scales (almost) linearly with weight. So the weight you send is load-bearing —49 send the exact intended weight at full precision, then round the returned50 `total_cost` to 2 decimals. `zone_distance` and `service_days` come straight51 from the response (do not recompute).5253Record shapes you will use constantly:54- product: `{sku, name, active(bool), category, supplier_id, unit_cost,55 weight_lb, safety_stock, overstock_threshold}`.56- inventory row: `{warehouse_id, sku, on_hand, reserved, quarantined,57 last_count_date}`.58- customer: `{customer_id, name, tier, margin_band, account_status, risk_flag}`.59 - `account_status` in {active, review_required, blocked}.60 - `risk_flag` in {none, credit_watch, fraud_watch}.61- supplier: `{supplier_id, name, region, quality_status}` with62 `quality_status` in {approved, watch, quality_hold}.63- order: `{order_id, wave, customer_id, warehouse_id, destination_zip,64 priority, required_date, shipping_speed, lines:[{line_id, sku, quantity,65 unit_price}]}`.66- PO: `{po_id, sku, supplier_id, warehouse_id, quantity, status, eta}` with67 `status` in {open, confirmed, received, cancelled}.68- incident: `{incident_id, supplier_id, sku, warehouse_id, incident_type69 (RMA|WORK_ORDER), severity (low|medium|high|critical), status (open|closed),70 open_date, close_date, resolution_cost, root_cause}`.71- BOM: `{bom_id, name, warehouse_id, target_date, components:[{sku,72 quantity_per_kit}]}`.7374---7576## 2. THE CORE INVENTORY RULE (USED IN MULTIPLE TASKS)7778**Effective (freely usable) available stock** at a warehouse:7980```81effective_available = on_hand - reserved - quarantined - safety_stock82```8384Reserved, quarantined, AND the safety-stock buffer ("normal operating buffer")85are all PROTECTED and must NOT be counted as available for new demand. This864-term formula was verified correct (it produced perfect scores on the87allocation and replenishment tasks).8889Critical detail: **DO NOT clamp `effective_available` to zero.** Keep it raw,90including negative values (e.g. `on_hand 1, reserved 0, quarantined 0,91safety 52` → `-51`). The reported "effective available" field and any gap math92(`gap = required - effective_available`) depend on the negative value. Clamping93to 0 understated gaps and cost score.9495Caveat for the "expedite queue" style task (Section 6): that task has a separate96`low_stock` vs `shortage` distinction and a standalone `safety_stock` concept,97so there `shortage` is judged on `on_hand - reserved - quarantined` versus the98order quantity; treat safety stock only as the low-stock threshold there. Use99the full 4-term protected formula for ALLOCATION and REPLENISHMENT tasks.100101For transfer sourcing, a donor warehouse's lendable quantity is its own102`effective_available` (same 4-term formula) — never lend protected stock.103104---105106## 3. CUSTOMER / ACCOUNT GATING (release decisions)107108Precedence for customer-driven holds (highest first):1091. `account_status == blocked` → account-level block.1102. `risk_flag == fraud_watch` → account-level block.1113. `risk_flag == credit_watch` → credit hold (account-level).1124. `account_status == review_required` → account-level review.1135. otherwise → none.114115Account/risk holds (blocked, fraud_watch, review_required, and credit_watch)116stop the order at the ACCOUNT level → these orders go on the "blocked orders"117list and their lines become `manual_review`.118119Product-master problems are LINE-ONLY, NOT account blocks:120- `product.active == false` → that line is `manual_review` with reason121 `inactive_product`, but the ORDER is NOT added to blocked_orders (unless it122 also has an account/risk hold). An order with one inactive line and other123 normal lines is `mixed_actions`, not blocked.124125---126127## 4. ALLOCATION / TRANSFER TASK SOP (verified 1.0)128129Goal: classify every order line in a wave as `ship | transfer | backorder |130manual_review`, emit transfer requests, blocked orders, per-order rollup, and a131summary.132133Per line:1341. Resolve customer gating (Section 3). If an account/risk hold applies →135 `action = manual_review`, `ship_quantity = 0`, `transfer_quantity = 0`,136 `backorder_quantity = 0`, `primary_reason` = the matching reason137 (`account_blocked | fraud_watch | account_review_required`). Add the order to138 `blocked_orders`.1392. Else if product inactive → `manual_review`, reason `inactive_product`140 (line only; order NOT blocked).1413. Else compute `req_ea = effective_available(requested_warehouse)` (raw, may be142 negative):143 - `req_ea >= qty` → `ship`, ship_quantity = qty, reason `none`.144 - else `ship_part = max(req_ea, 0)`, `remaining = qty - ship_part`:145 - Find ONE other warehouse whose own effective_available `>= remaining`146 (prefer the largest such donor; tie-break by warehouse_id ascending).147 If found → `transfer`: `ship_quantity = ship_part`,148 `transfer_from = donor`, `transfer_quantity = remaining`,149 reason `insufficient_effective_stock`. Emit a transfer_request row.150 - else → `backorder`: `ship_quantity = ship_part`,151 `backorder_quantity = remaining`, reason `insufficient_effective_stock`.152 - Choose a SINGLE source warehouse for a transfer line; leave any usable153 requested-warehouse quantity as `ship_quantity`.154155`primary_reason` enum: `none | account_blocked | account_review_required |156fraud_watch | inactive_product | insufficient_effective_stock`.157158Order rollup outcome enum: `ready_to_ship | needs_transfer | has_backorder |159manual_review | mixed_actions`:160- all lines ship → `ready_to_ship`.161- all lines manual_review → `manual_review`.162- any manual_review mixed with other actions → `mixed_actions`.163- only ship/transfer (≥1 transfer) → `needs_transfer`.164- any backorder (no manual_review) → `has_backorder`.165166Summary integer keys: total_orders, total_lines, ship_lines, transfer_lines,167backorder_lines, manual_review_lines, blocked_orders (count), transfer_units,168backorder_units.169170Ordering: `line_actions` and `transfer_requests` sorted by order_id asc then171line_id asc; `blocked_orders` and `order_rollup` sorted by order_id asc.172173---174175## 5. KIT REPLENISHMENT TASK SOP (verified ~0.83)176177Goal: for BOM builds at a planning warehouse, produce per-component coverage,178transfer requests, purchase requisitions, exclusions, and totals.1791801. `total_required[sku] = Σ over builds (build_quantity * quantity_per_kit)`.181 A SKU appearing in multiple BOMs sums across them.1822. `target_effective_available` = effective_available at the planning warehouse183 (4-term formula, RAW / may be negative). This is the reported field.1843. `gap = total_required - target_effective_available`.1854. `timely_po_qty` = sum of quantities of purchase orders that are186 **same planning warehouse**, status `open` or `confirmed`, AND187 `eta <= needed_by` (the component's earliest required build date). POs that188 are `received`, `cancelled`, at another warehouse, or arriving after the189 build date are NOT timely. `coverage_po_ids` = sorted ids of those timely POs.1905. Decision cascade per component (`final_action` / `exclusion_reason`):191 - `gap <= 0` and `target_effective_available >= overstock_threshold`192 → `overstock_excluded` / `target_overstock`.193 - `gap <= 0` (otherwise) → `no_action_stocked` / `stocked_no_gap`.194 - `target_effective_available >= overstock_threshold` (gap>0)195 → `overstock_excluded` / `target_overstock` (do not add to an196 already-overstocked SKU).197 - `timely_po_qty >= gap` → `timely_po_covered` / `timely_po_covers_gap`.198 - else cover `gap - timely_po_qty` first by TRANSFERS from other warehouses'199 effective_available (largest donor first, tie by warehouse_id asc):200 - fully covered by transfers → `transfer_only` / reason `none`.201 - residual remains → `purchase_required` / reason `none`, and create a202 purchase requisition for the residual quantity.2036. Purchase requisition: `supplier_id` from product master, `warehouse_id` =204 planning warehouse, `quantity` = residual, `needed_by` = component build date,205 `unit_cost` = product.unit_cost (2 dp), `extended_cost = unit_cost*quantity`206 (2 dp).2077. `excluded_components` list contains every component whose `exclusion_reason`208 is not `none` (`target_overstock | timely_po_covers_gap | stocked_no_gap`),209 with `supporting_po_ids` = the timely PO ids for `timely_po_covers_gap`,210 else `[]`.211212Summary:213- `component_count` = number of components.214- `total_purchase_units` = Σ requisition quantities.215- `total_purchase_cost` = Σ extended_cost (2 dp).216- `total_transfer_units` = Σ transfer quantities.217- `timely_po_covered_units` = Σ of the **gap covered by timely POs** for218 `timely_po_covered` components — i.e. the gap amount, NOT the full PO quantity.219 (Reporting full PO qty here cost score; report `min(gap, timely_po_qty)` = the220 gap.)221222Ordering: `component_plan`, `purchase_requisitions`, `excluded_components`223sorted by sku asc. `transfer_requests` sorted by sku asc, then quantity desc,224then from_warehouse_id asc. `kit_targets` sorted by bom_id asc.225226Open question (lower confidence): when a component has no gap AND is at/above227overstock_threshold, whether the label should be `overstock_excluded` vs228`no_action_stocked` — prefer `overstock_excluded` when229`effective_available >= overstock_threshold` since that condition is literally230true.231232---233234## 6. EXPEDITE-QUEUE / DISPATCH-CONTROL TASK SOP (partial)235236Goal: per order, report `inventory_status`, `customer_exception`,237`final_decision`, `next_action`, the three SKU exception lists, and a shipping238quote; plus a wave summary.239240Per-line inventory classification (this task tracks safety stock separately):241- `available = on_hand - reserved - quarantined` (do NOT subtract safety here).242- `inactive` line: product.active == false.243- `shortage` line: `available < quantity`.244- `low_stock` line: can fulfill but the buffer is breached (available below /245 dropping below safety_stock).246247Order-level `inventory_status` enum (priority high→low):248`inactive_and_shortage` (has both) > `inactive_sku` > `shortage` >249`low_stock` > `ready`.250251`customer_exception` enum mirrors Section 3:252`account_blocked | fraud_watch | credit_watch | review_required | none`.253254`final_decision` enum {ship_now, delayed_release, manual_review, backorder,255reject_hold} paired with `next_action` {release_to_pick, delay_and_monitor,256send_account_review, create_backorder, hold_credit_or_fraud,257escalate_product_master}. Natural pairings:258- account_blocked/fraud_watch/credit_watch → reject_hold / hold_credit_or_fraud.259- review_required → manual_review / send_account_review.260- inactive SKU → manual_review / escalate_product_master.261- shortage → backorder / create_backorder.262- low_stock → delayed_release / delay_and_monitor.263- clean & ready → ship_now / release_to_pick.264265Shipping quote: speed = the ORDER's `shipping_speed` (memo may explicitly say266"use the order's requested shipping speed"; quotes are required even when the267queue decision is not "release"). weight = Σ(product.weight_lb * quantity) over268all lines. Report `zone_distance`, `service_days` from the response and269`total_cost_usd` = round(total_cost, 2).270271Summary: `order_count`, `decision_counts` (one integer per decision enum value),272`total_shipping_cost_usd` (2 dp), and id lists `blocked_order_ids` (reject_hold),273`manual_review_order_ids`, `backorder_order_ids`, `inactive_sku_order_ids` — all274sorted ascending. Records sorted by order_id asc; SKU lists sorted asc.275276CAUTION (unresolved): this task scored low in training and was insensitive to277decision-precedence changes, meaning the dominant error was in the per-line278classification and/or shipping-cost basis rather than the decision mapping.279When you face this task, treat the available-stock formula and the shipping280WEIGHT basis as the highest-risk fields: double-check whether quarantine should281be subtracted and whether weight is the full Σ(weight_lb*qty). The precedence282ordering above is plausible but the score was driven by the numeric fields.283284---285286## 7. SUPPLIER INCIDENT SCORECARD SOP (verified 1.0)287288Filter incidents by `open_date` within the requested window (the `/incidents`289`start`/`end` params do this, inclusive both ends). Group by supplier.290291Per supplier row:292- `incident_count`.293- `incident_percentage` = count / (total filtered incidents) * 100, rounded to294 the requested precision (commonly 1 decimal). Denominator = the WHOLE filtered295 population, not per-type.296- `total_resolution_cost` = Σ resolution_cost (2 dp).297- `avg_duration_days` (2 dp). Duration per incident:298 - closed: `close_date - open_date` in calendar days.299 - open: `analysis_date - open_date` in calendar days.300- `rma_count` (incident_type RMA), `work_order_count` (WORK_ORDER).301- `open_incident_count` (status open).302- `severe_incident_count` = severity in {high, critical}.303- `recommendation_code` via the task's recommendation policy. Apply codes in the304 stated PRECEDENCE order (first match wins), e.g. a typical policy:305 - `ESCALATE_SUPPLIER`: quality_hold with ≥3 filtered incidents, OR any306 critical RMA (incident_type RMA and severity critical), OR ≥3 RMAs with307 ≥ a cost threshold.308 - `PROCESS_REVIEW`: WORK_ORDER count ≥3 AND greater than RMA count.309 - `WATCHLIST`: quality_status in {watch, quality_hold}, OR incident_count ≥4,310 OR total cost ≥ a threshold, OR severe_incident_count ≥2.311 - `MONITOR`: none of the above.312 Always read the exact thresholds and precedence from the task payload — they313 are spelled out and must be applied literally.314315Derived outputs:316- `top_escalation_suppliers` = supplier_ids whose code is ESCALATE_SUPPLIER,317 ordered by incident_count desc, then total_resolution_cost desc, then318 supplier_id asc.319- `highest_cost_supplier_id` = supplier with max total_resolution_cost320 (tie-break supplier_id asc).321- `highest_share_supplier_id` = supplier with max incident_count322 (tie-break supplier_id asc).323- summary: filtered_incident_count, supplier_count (suppliers with ≥1 incident),324 total_resolution_cost (2 dp), overall_rma_count, overall_work_order_count.325326Rows sorted by supplier_id asc.327328---329330## 8. SUPPLIER REPLENISHMENT-CONTROL SOP (partial)331332For each target supplier over an analysis window, compute the same incident333stats as Section 7 (filtered on `open_date` in the window):334`recent_incident_count`, `recent_rma_count`, `severe_or_critical_count`335(high|critical), `open_incident_count`, `affected_skus` (sorted unique),336`sample_incident_ids` (sorted, max 5). Compute these EXACTLY — they are the bulk337of the score and must match the raw filtered population precisely. Verify your338window endpoints and that you filter on `open_date`.339340`held_po_ids` per supplier = that supplier's `open`/`confirmed` POs that are341held because of the control decision. Top-level `held_po_ids` = sorted union;342`release_supplier_ids` = suppliers whose decision is `monitor_only`.343344Decision enum {freeze_new_replenishment, buyer_review_required, monitor_only}.345Lower-confidence policy (no fully-specified rule was given in training; reason346from quality_status + active/open risk):347- `freeze_new_replenishment`: supplier on `quality_hold` (strongest control).348- `buyer_review_required`: `watch` status, or unresolved/open incidents, or a349 critical incident on an otherwise-OK supplier.350- `monitor_only`: approved with no active risk.351Held POs follow the decision: freeze and buyer_review suppliers' open/confirmed352POs are held; monitor_only suppliers are released. If a task payload spells out a353decision policy, apply it literally instead of the heuristic above.354355Summary: suppliers_reviewed, freeze_count, buyer_review_count, monitor_count,356held_po_count, total_recent_incidents.357358---359360## 9. OUTPUT / FORMATTING CONVENTIONS (ALL TASKS)361362- Return ONLY the JSON object that matches `answer_template.json`. No prose.363- Include EVERY `required_top_level_key` and every `item_required_keys` field,364 even when empty (`[]`, `0`, `"none"`, or `null` per the template's enum).365- Respect each field's declared type: integer fields must be ints (not floats),366 enum fields must use an allowed value verbatim, currency fields are numbers367 rounded to 2 decimals.368- Set required constant fields exactly (e.g. `wave_id`, `task_id`,369 `required_value`).370- Apply the template's declared sort order to every list (commonly ascending by371 id/sku; some lists have multi-key orders — follow them precisely).372- SKU/id sub-lists are sorted ascending and de-duplicated.373- `effective_available`-style fields are reported RAW (can be negative); do not374 clamp unless the template says otherwise.375- Currency rounding is the LAST step (round each line, and round summary totals376 from the rounded/exact components consistently to 2 dp).377- Quote `total_cost` is rounded to 2 dp; `zone_distance` and `service_days` come378 from the quote response unchanged.379380---381382## 10. COMMON MISJUDGMENTS THAT COST SCORE383384- Clamping effective availability to 0 instead of keeping negative values385 (breaks gap math and the reported field).386- Forgetting to subtract `safety_stock` (the "normal operating buffer") from387 available stock in allocation/replenishment tasks.388- Counting `received`/`cancelled` POs, other-warehouse POs, or late-ETA POs as389 "timely" coverage. Timely = same warehouse, open/confirmed, eta ≤ needed_by.390- Reporting full PO quantity for "covered units" instead of the gap actually391 covered.392- Treating an inactive-product line as an account block (it is line-only;393 it does NOT put the order on the blocked list).394- Splitting a transfer across multiple source warehouses when the task wants a395 single source per line.396- Wrong incident date semantics: the window applies to `open_date`, inclusive;397 open incidents' duration runs to the analysis_date, closed incidents'398 duration runs to close_date.399- Misordering recommendation precedence (first matching tier wins) or using the400 wrong percentage denominator (use the whole filtered population).401- Sending the wrong weight or speed to the shipping quote (use the order's402 shipping_speed and the full Σ(weight_lb*qty)); recomputing zone/service_days403 instead of taking them from the response.404- Emitting floats where integers are required, or skipping required keys with405 empty values.