Cycle counts, not annual freeze
The NS inventory reconciliation flow runs a rotating cycle count: every warehouse zone is counted once every 30 days; every SKU lands on the list at least once per month. We never do annual full freezes — the warehouse can't go dark for a day, and cycle counts catch shrink + bin-error far faster anyway.
The discipline came in after R49 — senior architect review flagged inventory drift as the silent budget-killer. Pre-cycle-counts, we'd discover quantityonhand drift only when a SO short-picked. Post-cycle-counts, variance is caught in the same week.
Diagram: ns-inventory-reconciliation.html. The flow has 10 phases. One is STUB: physical-count autoflow via barcode scanner. Today, warehouse staff walk the zone with a paper tally sheet or type counts into the NS UI mobile app. There is no scanner integration.
Trigger conditions
- Weekly cron fires per warehouse zone (rotation guarantees every SKU within 30 days).
- An SO short-picks — ad-hoc count for the affected SKU and adjacent bins.
- Receiver rejects a PO line for short-ship — count the receiving bin to confirm.
- Post-incident: after a known damage event (forklift, water leak), the affected zone gets a manual count.
- Pre-bid: before a large bid award like B5875, count the relevant SKUs to confirm fulfillment capacity.
Auto-adjust if |variance_pct| ≤ 2% AND |variance_value| ≤ $100. Outside that band, HITL fires. Both bars must hold — a 0.5% variance on a $50K SKU is still a $250 swing and gets HITL.
Zone B count — Monday morning
Monday 06:00. The weekly cron picks Zone B (cold dock, 47 SKUs). Floor team (Marcus + temp) walks the zone with the paper tally sheet (STUB — no scanner). Counts entered into NS UI by 09:30. The variance engine runs at 09:35.
46 SKUs match within 2% / $100 — auto-adjusted, NS push queued, D1 mirror updated by 09:38. One outlier: SKU 21044 — Right Start Foods Egg Patty shows physical 184 cases, on-hand 142 cases — +29.6%, $1,470 value. HITL fires. Card lands at /proposed-actions.html with the variance, the prior-30-day txn list, and a suggested reason ("partial pallet not booked on PO receipt 3 days ago, likely receiving timing"). Mike taps Approve at 10:14. NS adjustment posted by 10:16. reflexion_log row written; events.inventory.adjusted fires. Customer health watcher consumes the event for the affected customer's allocation outlook.
The ten beats
-
01
Cycle count scheduled
Weekly cron per warehouse zone selects a count list. Cron rotates zones so every SKU is counted within 30 days. The list is materialized into
cycle_count_queuewith status='pending'. -
02
Physical count — STUB autoflow
Warehouse staff walks the zone and records counts. Today: paper tally sheet or NS UI mobile keyboard entry. Barcode scanner integration is on the long-term roadmap but not wired — no scanner hardware deployed, no scanner-to-NS path. This is the single highest leverage automation gap in inventory ops.
-
03
Variance detection
Diff
physical_qtyvsinventory_balance.quantityonhandper item × location. Computevariance_pctandvariance_abs(absolute units and dollar value at current cost). -
04
Threshold branch — auto or HITL
If
|variance_pct| ≤ 2% AND |variance_value| ≤ $100, route to auto-adjust. Otherwise route to HITL. Threshold configurable inguardrails. -
05
HITL stage — for outliers
A
proposed_actionsrow is inserted withaction_type='inventory_adjustment'. Payload containsitem_code, location, on_hand, physical_qty, variance, suggested_reason. The suggested_reason field looks at recent transactions for hints (PO receipt timing, recent damage flag, etc.). -
06
Mike approves at /proposed-actions.html
Admin reviews variance and reason. X-Edit-Token required for confirm. Approval flips
proposed_actions.status='approved'via the R560 atomic claim. -
07
NS push — inventoryadjustment
Approval drops a row in
ns_pending_pushestargeting record_type='inventoryadjustment'.PushMutexDOdrains and writes via the NS RESTlet OAuth1. Retry policy: 3 attempts, exponential backoff. -
08
D1 mirror update
inventory_balance.quantityonhandreflects the new value. Reservation rows recomputed. Sync engine confirms NS parity on the next 5-min sync. -
09
Audit trail
reflexion_logrow written with reason + delta + approved_by.events.inventory.adjustedfires onto the event ledger. Customer health watcher, anomaly detector, and timeline UI all consume this event. -
10
Variance report
The admin-dashboard tile surfaces last 30 days of variances by zone, item, and value. Patterns inform the next cron schedule — if Zone B keeps producing outliers, the cron raises its weight.
What's different after a count
- NS and D1 agree on
quantityonhandper SKU per location. - Every adjustment has a reason in
reflexion_log. - Patterns (recurring outliers, zone-level drift) inform the next cron.
- Downstream allocation, customer health, and bid promise dates reflect the true inventory.
What can go wrong
Paper tallies introduce transcription errors. Mitigation: floor lead reviews the tally before NS entry. Detection: HITL rate > 5% of count items indicates systemic miscounting. Long-term fix: scanner integration with NS UI mobile.
If picking happens mid-count, physical_qty becomes a moving target. Mitigation: zone-level pick hold for the count window. Detection: variance + pick-list timestamps that overlap the count window.
D1 has the adjustment, NS doesn't. The reconciliation cron at 0 */15 * * * catches this within 15 min and re-enqueues. Manual recovery: POST /admin/ns-push/retry?action_id=<id>.
If many counts cluster just under the threshold, real drift hides. Detection: variance histogram on the admin-dashboard tile. Recovery: tighten the threshold or trigger ad-hoc HITL review.
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Cycle count cron | wrangler.toml — 0 6 * * 1 |
| Count queue | cycle_count_queue (D1) |
| Variance engine | src/index.ts cycle_count handler |
| Thresholds | guardrails.inventory_variance_pct, _value |
| HITL action_type | proposed_actions.action_type='inventory_adjustment' |
| NS push record | record_type='inventoryadjustment' |
| Event emitted | events.event_type='inventory.adjusted' |
| Audit row | reflexion_log entity_type='inventory_adjustment' |
| STUB — scanner | no scanner hardware; paper/keyboard only |