Skip to main content

SOP: Picking

Document ID: WMS-PICK-001 Version: 1.0 Effective date: 04/30/2026 Owner: Warehouse Operations Manager Next review: [six months from effective date] Applies to: Floor staff working pick tasks on a Zebra TC22; managers monitoring the pick queue


1. Purpose

This procedure governs how an allocated order becomes physically picked — generating the pick list, walking the floor in pick-sequence order, scan-verifying the location and item at each stop, confirming the pick (or marking short), and staging the picked goods into a pick bin for handoff to packing. Picking is the first physical-touch step in the outbound flow and the most time-consuming activity in the warehouse on a per-order basis.

Allocation (per WMS-INV-006 §4.1) decides what gets picked from where. This SOP picks up at order status ALLOCATED and walks through to status PICKED (or PARTIALLY_PICKED if any item went short). The downstream packing flow is in WMS-PACK-001.

2. Scope

In scope:

  • The pick queue at /pick — what orders are pickable, who's already on them, priority filtering
  • Generating a pick list via POST /fulfillment/:orderId/pick (creates the WorkTask of type PICKING)
  • Walking the pick session at /fulfillment/:orderId — the two-phase scan flow (location → item)
  • Scan-driven and manual confirm paths
  • Denomination mode: scanning multiple units per item to make case-pack picking faster
  • Short-picking when physical stock doesn't match allocation
  • Staging into a pick bin at task completion
  • The events written to task_events and the SSE feed picker UIs subscribe to

Out of scope:

  • Order allocation — see WMS-INV-006 §4.1 (this SOP starts after allocation succeeds)
  • Pick bin behavior beyond initial creation at task completion — see WMS-PACK-001
  • Packing, shipping, label printing for the package — see WMS-PACK-001 onwards
  • Multi-bin / multi-package pick orchestration — covered as part of WMS-PACK-001 (the pick bin auto-creates here; bin lifecycle is a packing concern)
  • Reassigning a stuck pick task — there is no reassign UI today; see §8

3. Roles & permissions

API enforcement, verified against routes:

  • POST /fulfillment/:orderId/pick (generate list), POST /fulfillment/:orderId/pick/:taskItemId/confirm (per-item confirm), POST /fulfillment/:orderId/pick/confirm-all (bulk confirm) — auth only, no role gate
  • The /pick route in the React router gates on STAFF_ROLES — meaning STAFF, MANAGER, ADMIN, SUPER_ADMIN can access the page; READONLY cannot
RoleView pick queueGenerate pick listWalk a pick taskConfirm pick / shortBulk confirm
READONLY— (router blocks at /pick)
STAFF
MANAGER
ADMIN
SUPER_ADMIN

Operational expectations (not technically blocked):

  • A picker walks one pick task at a time. The system doesn't block parallel picks by the same user, but two simultaneous task confirms from the same person create messy event timelines and confused multi-bin state.
  • Self-assignment is the norm — no central dispatcher. Picker taps an order in the queue, becomes the de-facto worker on it, walks the floor.
  • Bulk-confirm (/pick/confirm-all) bypasses scan verification entirely. Reserve it for known-clean pick lists where scan-verify isn't operationally required (e.g., admin bypass on a stuck task that's already 95% complete and the picker walked away). It is not the primary path.

4. Procedures

4.1 The pick queue

Use when: Starting your shift, looking for the next order to work, or checking who's on what.

Steps:

  1. Open /pick. The page lists orders with status IN ('ALLOCATED', 'PICKING'), sorted by priority then age.
  2. Each row shows:
    • Order number
    • Status badge — Ready (status ALLOCATED, no one's on it) or Picking (status PICKING, someone's working it)
    • Priority badge for URGENT / HIGH (red border on the card)
    • Customer name, item count, time since order placed (timeAgo)
    • Progress bar for in-progress orders ({picked}/{total})
  3. Tap the order. The page navigates to /fulfillment/:orderId — the unified pick → pack → ship workflow page.
  4. If the order is ALLOCATED (no pick task yet), the fulfillment page calls POST /fulfillment/:orderId/pick to create the pick task before showing the first pick item. If the order is already PICKING (someone's in flight), the page resumes from the next pending item.

What's surfaced and what's not:

  • The queue does not show who is currently picking an in-progress order. If you tap an in-progress order, you become the active picker by default — there's no warning that someone else may be on it.
  • There is no filter for "tasks assigned to me" because task assignment is implicit — the first user to hit confirmPickItem on a task becomes the recorded userId on those events, but there is no assignedToId field on the WorkTask for picking tasks.

4.2 Generating the pick list

Use when: An order is ALLOCATED and you tap it from the queue. (You don't normally call this endpoint directly — the fulfillment page handles it.)

Prerequisites (enforced by PickingService.generatePickList):

  • Order status is one of PENDING, CONFIRMED, READY_TO_PICK, or ALLOCATED. If the order is in any other status (already PICKING, PICKED, CANCELLED, etc.), the endpoint throws Cannot start picking: order is {status}. Expected one of: PENDING, CONFIRMED, READY_TO_PICK, ALLOCATED.
  • Order has at least one allocation. Otherwise: No allocations found for order {orderNumber}. Allocate inventory first.
  • No active pick task already exists for the order. Otherwise: Active pick task {taskNumber} already exists for this order.

What this writes (one transaction):

  • A new WorkTask row with:
    • taskNumber: PICK-{orderNumber}-{shortId} (per generateTaskNumber helper)
    • type: PICKING
    • status: PENDING
    • priority: 2 if order.priority==='EXPRESS' else 1 if order.priority==='RUSH' else 0
    • totalItems: <allocation count>
    • orderIds: [orderId], totalOrders: 1
  • One TaskItem per allocation, sorted by location.pickSequence ASC (nulls last via fallback 9999):
    • taskId, productVariantId, locationId, inventoryUnitId, allocationId, orderItemId
    • quantityRequired: <allocation quantity>
    • quantityCompleted: 0
    • sequence: 1, 2, 3... (the walking order — same as pickSequence ascending)
    • status: PENDING
  • Two fulfillment_events rows: order:processing and picklist:generated (with the per-item summary in the payload).

Result:

  • The order doesn't transition to PICKING immediately. It transitions when the first item is confirmed (per §4.4) — the repo flips WorkTask.status: IN_PROGRESS and the order tracks separately.
  • The fulfillment page now has a currentPickItem to show the picker.

4.3 The two-phase scan flow

Use when: You're on /fulfillment/:orderId after generating or resuming a pick list.

The flow alternates between two scanPhase states:

  1. scan_location — the page shows the next pick item with its expected location (zone, aisle, rack, shelf, bin from the Location row, plus the location's barcode). Walk to that location.
  2. scan_item — after the location is scanned, the page accepts the next scan as the item barcode. The picker physically takes the unit from the bin and scans it before moving on.

Steps for a single pick:

  1. The screen shows the current item: SKU, product image, variant name, location label (e.g., A-01-02 / Zone A / Aisle 1), required quantity.
  2. Walk to the location. Scan the location's barcode.
  3. The page shows feedback: ✓ Location scanned — now scan item. The scanPhase flips to scan_item. (If the location has no expected barcode set, the page shows No location required — scan item and skips this phase.)
  4. Pick the unit physically. Scan the unit's barcode.
  5. The page calls POST /fulfillment/:orderId/pick/:taskItemId/confirm with { quantity: <required>, scannedLocationBarcode, scannedItemBarcode, binId }.
  6. The server records the pick (per §4.5). The page advances to the next item, scan phase resets to scan_location.

Important behaviors:

  • The location-scan barcode is just stored on the request. The server records it on the task item (locationScanned: true) but does not validate it against the expected location's barcode. Same for the item scan. The picker's flow gates verification client-side; the audit trail is "this scan happened" not "this scan matched."
  • If a barcode looks like a bin barcode (matches a pickBin.barcode for this order), the page treats it as a "switch active bin" command rather than a location/item scan. This is for multi-package orders — see §4.7.

⚠ Scan validation is client-only. A picker who knows what they're doing can scan any barcode-shaped thing in either phase and the server will accept it. The locationScanned and itemScanned flags on the task item are based on whether any scan happened in that phase, not whether the scan was correct. This is an honest limitation; if you suspect a picker is gaming the flow, the audit trail won't catch it directly. Manager spot-checks during walks are the control. See §8.

4.4 Denomination mode (multi-unit per scan)

Use when: The order needs multiple units of a single SKU and you're scanning each unit one at a time (rather than one scan = full quantity).

How it works:

  1. The page maintains a scanMultiplier state. Default is 0 (one scan = full quantity).
  2. The picker can set scanMultiplier to a positive number (typically 1, meaning each scan = +1 to the running total). The UI exposes this; tap +1, +2, etc., depending on how many units come per scan.
  3. Each item-scan in scan_item phase increments a pickAccumulator for the current taskItemId. The page shows {sku}: {n}/{required} scanned.
  4. When the accumulator reaches the required quantity, the page auto-calls confirmPick and advances to the next item.

Why this matters:

  • For a 24-pack of an item where each individual unit has its own barcode, the picker can scan each unit (1+1+1...) and the system accumulates rather than firing 24 separate "pick complete" events.
  • The accumulator is local UI state — if the picker refreshes the page, the accumulator resets. The server doesn't know about partial accumulation; it only knows when a full confirm fires.

4.5 Confirming a pick: full quantity vs. short

Full pick (the happy path):

The auto-flow at end of §4.3 step 5. Server-side, confirmPickItem (per picking.service.ts:213 and picking.repo.ts:323) runs in a transaction:

  1. Updates TaskItem: status: COMPLETED, quantityCompleted: <quantity>, completedBy: <userId>, completedAt: now, locationScanned, itemScanned.
  2. Updates the linked Allocation: status: PICKED, pickedAt: now.
  3. Increments OrderItem.quantityPicked by the picked quantity.
  4. If the underlying InventoryUnit.quantity <= 0 after the pick (i.e., the bin's now empty), updates the unit's status: PICKED. (Otherwise the unit stays AVAILABLE for future allocations.)
  5. Updates the WorkTask: completedItems: <count>, status: IN_PROGRESS, startedAt: <existing or now>.
  6. Writes a task_events row: eventType: ITEM_COMPLETED, with sku/locationName/quantity in the payload.
  7. If this was the last item on the task, also updates WorkTask.status: COMPLETED, completedAt: now, completedOrders: 1, and writes a TASK_COMPLETED event.

Short pick:

Use when: You arrive at the location and there's not as much physical stock as the allocation expected. Maybe 5 of 7 are there, or none.

Steps:

  1. Pick what's physically available. Don't overstate.
  2. The UI exposes a Confirm {n} (Short) button when in denomination mode with a partial count — it sends quantity: <accumulated>, which is less than quantityRequired.
  3. Outside denomination mode, the UI shows the All {required} confirm; for a short, the picker can manually call confirmPick with a smaller quantity (the page exposes this via the confirm-with-accumulator path; if not, see §7 troubleshooting).

Server-side, when quantity < quantityRequired:

  1. TaskItem.status: SHORT, quantityCompleted: <picked qty>, shortReason: "Short pick: {picked}/{required}".
  2. Linked Allocation.status: PARTIALLY_PICKED (not PICKED).
  3. OrderItem.quantityPicked increments by the picked quantity (not the required).
  4. Same task-level updates as full pick. The task_events row uses eventType: ITEM_SHORT.

Result of a short pick:

  • The order does not automatically backorder the missing quantity. The order's OrderItem.quantity stays at the original, but quantityPicked < quantity. The downstream packing/shipping flow handles the partial — see WMS-PACK-001.
  • The picker keeps walking. Subsequent items confirm normally.
  • When the task completes, it has both completedItems (everything that was confirmed in any state) and shortItems (subset that went short).

⚠ Short pick does not automatically write an InventoryAdjustment. The system records the short on the task item and the allocation, but the inventory variance — "the system says 5, you found 3" — needs a separate cycle count (per WMS-INV-002) to formally reconcile. Until then, the system thinks the missing 2 units are still at that location, and may try to allocate them to the next order. The two-step recovery: (a) finish the short pick to release the order, (b) immediately cycle-count the bin per WMS-INV-002 to fix the system stock. Skipping step (b) propagates the short to future orders.

4.6 Bulk confirm (admin shortcut)

Use when: Rare. A pick task is mostly complete and the remaining items can't be physically scanned for some reason — picker walked away mid-task and you need to close it for downstream packing.

Steps:

  1. (No mounted UI button on the picker page — see §8.) Call POST /fulfillment/:orderId/pick/confirm-all.
  2. Server-side, confirmAllPickItems iterates every PENDING task item and confirms each with quantity: quantityRequired, locationScanned: false, itemScanned: false.

Caveats:

  • No barcode verification at all. Both flags record false on every item.
  • No short-pick option. Every remaining item confirms at full required quantity. If physical stock is short, the system records full pick anyway — the resulting allocation says PICKED but the picker hasn't actually picked. This will surface as a packing variance later.
  • One task_events row per item. Even though it's bulk, the events are individual ITEM_COMPLETED rows; the audit trail is preserved per item.

⚠ Bulk confirm is dangerous. It writes the same database state as a real pick session, but with no scan verification. Use it only when (a) you can physically verify the units are correctly picked, or (b) you accept that downstream packing/shipping will catch any discrepancy. The audit trail will show locationScanned: false and itemScanned: false on every item — that's the manager's flag that this was a bulk confirm. If you find yourself reaching for it routinely, that's an operational problem, not a feature problem.

4.7 Pick bin staging (handoff to packing)

When confirmPickItem results in taskComplete: true, the service also runs createPickBin(orderId, pickTaskId, userId):

  1. Generates a bin number via generateBinNumber() and a printable barcode.
  2. Creates a PickBin row associated with the order and task.
  3. Returns the bin (id, binNumber, barcode) in the PICKLIST_COMPLETED event payload.

The picker physically transfers the picked goods into the bin (typically a tote, but can be a bag/box for small orders). The bin sits at the staging shelf for packing pickup.

For multi-package orders (orders that need to ship in multiple boxes — typical for large orders or when ShipEngine's box recommender suggests splitting), the pick bin model expands. The fulfillment page initializes multiple bins via /fulfillment/:orderId/bins/init before picking starts, and each pick scan can target a specific bin via binId in the confirm body. This is covered in WMS-PACK-001 — for the pick procedure, the picker just scans whatever bin the page shows as "active" and continues.

5. Reference

5.1 The WorkTask of type PICKING — fields used

Created per §4.2:

FieldValue
taskNumberPICK-{orderNumber}-{shortId}
typePICKING
statusPENDINGIN_PROGRESS (first pick) → COMPLETED (last pick)
priority2 (EXPRESS) / 1 (RUSH) / 0 (STANDARD)
totalItemsNumber of allocations
completedItemsIncrements on each confirm
shortItemsIncrements on each short-pick
orderIds[orderId]
totalOrders1
startedAtFirst confirm
completedAtLast confirm

5.2 The TaskItem per pick — fields used

FieldValue
taskIdThe PICK task
productVariantIdThe SKU's variant
locationIdThe bin to pick from
inventoryUnitIdThe specific unit (FK to Allocation.inventoryUnitId)
allocationIdThe reservation made at order-allocation time
orderItemIdBack-reference to the order line
quantityRequiredWhat allocation reserved
quantityCompletedWhat the picker confirmed (≤ required for shorts)
sequence1..n in pick-walk order (by location.pickSequence ASC)
statusPENDINGCOMPLETED or SHORT (or unused: SKIPPED)
locationScannedTrue if any scan happened in scan_location phase
itemScannedTrue if any scan happened in scan_item phase
shortReason"Short pick: {qty}/{required}" if short, null otherwise
completedBy, completedAtUser and timestamp

5.3 The task_events audit row

Each pick action writes one row:

Event typeWhen
ITEM_COMPLETEDFull pick confirm
ITEM_SHORTShort-pick confirm
TASK_COMPLETEDLast item of the task confirmed

task_events.data is JSON containing quantity, sku, locationName, isShort. The picker's userId is the row's userId.

The TaskItem.status enum has SKIPPED as a value but no code path writes it — same dead-enum pattern as elsewhere (see WMS-INV-007 §5.1, WMS-INV-005 §5.1). If you see SKIPPED, someone wrote it manually.

5.4 SSE events for live picker UIs

The picking service emits these events through the fulfillment SSE stream (useFulfillmentStream):

EventPayload includesWhen
ORDER_PROCESSINGorderNumber, taskIdWhen pick list generated
PICKLIST_GENERATEDtaskId, taskNumber, items[], totalItemsWhen pick list generated
PICKLIST_ITEM_PICKEDtaskItemId, sku, locationName, quantity, isShort, progressEach confirm
PICKLIST_COMPLETEDtaskId, completedItems, shortItems, binLast confirm
ORDER_PICKEDorderNumberLast confirm

Multiple managers / monitors can connect to the SSE feed and watch picks happen in real time. Useful for live throughput dashboards and supervisor visibility — the events are also queryable from fulfillment_events after the fact.

  • WMS-INV-006 §4.1 — Order allocation (the upstream that produces the pickable allocations)
  • WMS-INV-002 — Cycle counts (the recovery for inventory variance after a short-pick — see §4.5 callout)
  • WMS-INV-003 — Floor count (creates SkuLocationMap rows that allocation uses to choose which location to pick from)
  • WMS-INV-004 §5.3 — Pick sequence (the field that orders the pick walk within a zone)
  • WMS-PACK-001 — Packing (the immediate downstream consumer of the pick bin)
  • WMS-PICK-002 — Pick bin labels (when written)
  • WMS-PICK-003 — Short-pick recovery (when written — the formal procedure for what to do after a short pick)
  • WMS-AUD-002 — Shrinkage investigation (where pick variance trends get analyzed)

6. Audit & compliance

The picking flow has the most complete audit trail of any inventory operation in the WMS:

  • Per-item events — every confirm writes a task_events row with userId, timestamp, sku, location, quantity, short flag.
  • Per-order events — every status transition writes a fulfillment_events row visible across the fulfillment dashboard.
  • Per-allocation statusAllocation.status flips ALLOCATED → PICKED (or PARTIALLY_PICKED) atomically with the pick confirm, with pickedAt timestamp.
  • Per-task aggregatesWorkTask.completedItems, shortItems, startedAt, completedAt are the per-task summary.

What's still missing:

  • Scan correctness verification. The flags say "a scan happened in this phase," not "the scan matched the expected barcode." For audit-grade scan-verification, the server would need to load the expected barcode at confirm time and compare. Today, that's not implemented.
  • Reassignment trail. A pick task can be silently taken over by another user — the latest userId on the events wins. There's no assignedToId or reassignment log.
  • Pause/resume. No procedure for pausing a pick task in progress. Walking away leaves the task in IN_PROGRESS indefinitely; the next picker who taps it on /pick becomes the de-facto worker.

Manager weekly review:

  • Pull WorkTask WHERE type = 'PICKING' AND status = 'IN_PROGRESS' AND startedAt < now() - 4 hours — stale pick tasks. Each one was abandoned mid-pick.
  • Pull task_events WHERE eventType = 'ITEM_SHORT' AND createdAt > now() - 7 days grouped by productVariantId — recurring shorts on a SKU indicate either chronic over-allocation or stocking issues. Cross-reference with cycle-count results per WMS-INV-002.
  • Per-picker: task_events rows by userId, count of ITEM_COMPLETED vs. ITEM_SHORT. A picker with high short rate either gets the worst SKUs or has technique issues — coaching material.

Quarterly governance:

  • Mean (completedAt - startedAt) / totalItems per picker — pick speed.
  • Bulk-confirm rate (/pick/confirm-all calls). Should be near zero. If trending up, investigate.
  • locationScanned: false rate. Should be near zero. If high, scanners are broken or pickers are bypassing.

7. Troubleshooting

SymptomCauseResolution
Cannot start picking: order is PICKING (HTTP 400)Order already has an active pick taskResume by tapping the order in /pick (it shows Picking badge). Don't try to regenerate.
No allocations found for order {orderNumber} (HTTP 400)Allocation was never run, or it returned zero matchesRun allocation from the backorders page (per WMS-INV-006 §4.4) or from the order detail page.
Active pick task {taskNumber} already exists (HTTP 400)Same as above — one task per order ruleResume the existing task. If the task is stuck (picker abandoned), check §7 row 8.
Picker scans the location, but the page doesn't advance to scan_itemScan keyboard input wasn't captured (page wasn't focused), or the barcode looked like a bin barcodeTap the scan input to refocus. If a bin-barcode collision (rare), see §7 row 5.
Picker scans a bin barcode by mistake during scan_locationThe page interprets it as "switch active bin"If a multi-package order, this may be intentional. If not, scan the actual location barcode to advance.
quantityCompleted was set higher than quantityRequiredBug — the API does not validate thisShould not happen via the UI, but if it does, escalate to IT. The downstream pack flow may show a discrepancy.
Order shows PICKED but OrderItem.quantityPicked < quantityOne or more items went short; the order is PARTIALLY_PICKED not PICKED. UI label may be confusingCheck the order's actual status in the DB. Status PICKED means everything fully picked; PARTIALLY_PICKED means at least one short.
Picker walked away mid-task; new picker can't see "where to resume"The task is IN_PROGRESS with some items COMPLETED and some PENDING; the page should show the next PENDING item by sequenceNew picker taps the order from /pick; the fulfillment page resumes from the next PENDING item. If it doesn't, the task is corrupted — escalate.
Bulk confirm wrote PICKED status but stock isn't actually in the binPer §4.6 callout, bulk confirm has no scan verificationCycle-count the affected location per WMS-INV-002. The discrepancy will show in the count and produce a formal InventoryAdjustment per WMS-INV-007.
Short-pick recorded but stock variance not correctedPer §4.5 callout, short pick alone doesn't write an adjustmentRun cycle count immediately. WMS-INV-002 §4.x.
Two pickers somehow worked the same orderNo mutex on pick task — first to confirm winsWhichever picker confirmed last is recorded on those events. Manager review the task_events to identify the actual worker. Operationally, coordinate via radio/Slack to prevent.

8. Escalation

  • Add real scan validation in confirmPickItem. The expected barcodes are loaded into the UI; the server has the data. A 10-line addition to compare scanned vs. expected (and reject mismatches) closes the audit gap. Engineering ticket — high audit value, low effort.
  • Build pause / pick-task reassignment. No UI for either today. A picker abandoning a task today leaves it stuck IN_PROGRESS and the next person silently takes over. A /admin/pick-tasks page with reassign and pause CTAs would close that hole. Cross-reference with WMS-AUD-001 (where pick stuckness gets investigated).
  • Auto-create cycle count on short pick. A short pick is a 100% reliable indicator of inventory variance. Auto-enqueueing a cycleCountTask of type LOCATION for the affected bin would force the variance to be reconciled rather than relying on operator discipline. Engineering ticket.
  • Mount the bulk-confirm UI behind a manager role check. Today the endpoint is auth-only. If you want it to stay (it has legitimate uses), add MANAGER+ role enforcement so STAFF can't accidentally bypass scan-verify on a whole task.
  • Use the dead SKIPPED enum value. The schema has TaskItemStatus.SKIPPED but no code writes it. A "skip this item" UI for irretrievable shorts (e.g., the bin literally collapsed) would let pickers continue without confirming, and the audit trail would show the skip explicitly.
  • Stuck pick task investigation. WMS-AUD-001 (when written) — pull stuck tasks weekly, walk them physically, decide reassign / cancel / complete by hand. Until that SOP exists, the warehouse manager's spreadsheet.
  • Suspected scan bypass / fraudulent picks. Manager spot-checks during the day are the only control today. Without server-side scan validation, a picker entering bogus barcodes will not be caught by the system. Coaching, cameras, or technical fix per first bullet.

9. Revision history

VersionDateAuthorChanges
1.0[DATE][NAME]Initial release. Documents the full picker flow grounded in apps/web/src/pages/pick/index.tsx (queue), apps/web/src/pages/fulfillment/[id].tsx (the actual pick session UI, despite the path being under /fulfillment not /pick), apps/api/src/routes/fulfillment.routes.ts (endpoints), packages/domain/src/services/picking.service.ts (generatePickList, confirmPickItem, createPickBin), and packages/db/src/repositories/picking.repo.ts (the transactional state writes). Documents the two-phase scan flow (scan_locationscan_item), denomination mode (scanMultiplier + pickAccumulator), short-pick path (status: SHORT, Allocation.status: PARTIALLY_PICKED), and the auto-created pick bin at task completion. Documents three real audit gaps: (a) scanned barcodes are not server-validated against expected — the flags say "a scan happened" not "it matched"; (b) short-pick alone does not write an InventoryAdjustment — formal reconciliation requires a follow-on cycle count per WMS-INV-002; (c) pick task assignment is implicit (latest user on events wins; no assignedToId, no reassignment log, no pause). Documents two dead schema values: TaskItemStatus.SKIPPED (never written) and the unused assignedToId pattern. Documents bulk confirm (/pick/confirm-all) as the admin-bypass path with explicit warning. Cross-references WMS-INV-002 (cycle count for short-pick recovery), WMS-INV-003 (SkuLocationMap), WMS-INV-004 §5.3 (pick sequence), WMS-INV-006 (allocation), WMS-PACK-001 (downstream), WMS-AUD-001 (pick stuckness — when written). Code references: apps/web/src/pages/fulfillment/[id].tsx:264, 635-720, 882+, 2799-2845, picking.service.ts:103-317, 323+, picking.repo.ts:323-440, fulfillment.routes.ts:103-141, 143-167.