The production side, two categories
The NS work order lifecycle is the production side of the transaction at GFS — the way the company assembles or builds finished goods. Per Mike's actual process, every WO falls into one of two categories.
Path 1 — WO created FROM a Sales Order. One or more lines on a customer SO require assembly. A Work Order is created FROM the SO so records stay linked end-to-end (workorder.createdfrom = so.id). Components are reviewed, production runs, Assembly Build is entered in NetSuite (Mike approves at /assembly-build.html), finished goods are added to inventory at the correct location, then the SO is fulfilled. Item Fulfillment fires the ★ automation alert to Finance, who reviews, invoices, monitors terms, applies payment, and closes the order. If the FG is already on hand, the shortcut is to fulfill from inventory and close the WO marked unused.
Path 2 — WO created for inventory build (for stock). When the company builds product into inventory for future sales / sell-through. WO is entered standalone in NetSuite (no parent SO), item / qty / location / components confirmed, production runs, Assembly Build completed, FG added to inventory, components consumed, WO closes naturally. No customer side. No Finance handoff on the WO itself — Finance enters later when a future SO sells the FG.
Diagram: ns-work-order-master.html. Path detail docs: Path 1 from sales order · Path 2 inventory build. Open TBD: Work Order customer PO# field — pending Mike's review (expected pattern bodyFields.memo).
Trigger conditions
- Customer SO contains a line with
itemtype = Assembly— Path 1. - An assembly's
quantityavailabledrops below itsreorder_point— Path 2. - Forecast / planned production for stock — Path 2.
- Bid pre-build ahead of a QBL (e.g. B5875 release order coming) — Path 2.
- USDA entitlement drawdown commit (e.g. barrel cheddar processing) — Path 2.
- R&D pilot run validating a new BOM — Path 2.
If the WO is created from a customer SO assembly_line, it goes Path 1. Everything else (stock build for future demand) goes Path 2. Operator-driven today; auto-classifier on /api/wo/create is a STUB.
On Path 1, after Item Fulfillment fires, an automation alert routes to Finance. Same shape as the SO master's Finance handoff — the single named bridge between Operations and Finance for assembly-driven orders.
Mike's notes confirmed customer PO# threading on SO (otherrefnum), PO (memo + lineFields.links[].tranid), Invoice (otherrefnum), Vendor Bill (memo / tranid / initialtranid). The WO body field is the only TBD. Expected pattern: bodyFields.memo. Every WO Schema reference flagged TBD until confirmed.
Two builds in one day
Friday morning. Driscoll SO #1217 (otherrefnum "72622") includes 1 assembly line for Melt Mates kit qty 24. Operator dispatches Path 1; WO-4471 created with createdfrom = 1217. Component review shows BOM components on hand at Heartland (location 3). Production runs; crew completes the build with 0 waste. Mike approves at /assembly-build.html; NS posts AB-1881 (createdfrom = 4471, quantity_built = 24). FG credit: inventory_balance.quantityavailable += 24 for MELT-MATES-KIT-A at location 3. WO close: status='Built'. SO fulfilled: IF-2210 (createdfrom = 1217, otherrefnum "72622"). ★ Automation alert fires to Finance. Finance posts CINV-9032 for $18,420 Net 30. Payment received + applied; SO closed.
Same day. RSF-BURR-KIT on-hand = 0, reorder_point = 240, B5875 QBL approaching. Operator dispatches Path 2; WO-4499 created standalone (createdfrom = NULL), assemblyitem "RSF-BURR-KIT", quantity 480, location 3, custbody_build_reason = "bid_pre_build", memo = "bid B5875 pre-build for QBL" (operator note — no customer PO# because no parent SO). Item / qty / location / components confirmed. BOM lookup; components on hand. Production runs; crew completes build for 472 (8 waste). Mike approves Assembly Build (AB-1902). FG credit: inventory_balance.quantityavailable += 472 for RSF-BURR-KIT at location 3. Components consumed (WkOrdIss per line). WO closes naturally (status='Built'). FG remains available for future SOs. No Item Fulfillment, no Finance handoff on the WO. Finance enters later when the B5875 release order actually sells this FG.
Intake → classify → build → close
Common intake steps run first. Then the WO branches into one of two paths based on origin. Both paths share the common production lane (components → build → Assembly Build → FG to inventory). Path 1 continues into SO fulfillment + Finance; Path 2 closes at FG availability.
Common steps (every WO)
-
01
WO intake (production side)
Production request enters NetSuite. Sources: upstream SO with assembly_line, or inventory build need (reorder, forecast, bid pre-build, USDA, R&D pilot).
-
02
Classify WO origin
Operator (today) classifies by origin. SO-driven assembly → Path 1. Stock build for future demand → Path 2. Auto-classifier is a STUB.
Path 1 — WO created FROM Sales Order
-
1.1
WO created FROM SO
For each assembly line, a Work Order is created with
workorder.createdfrom = so.id— preserves SO ↔ WO link end-to-end. Alternate: if FG already on hand at location, fulfill SO from inventory + close WO marked unused. -
1.2
Component availability reviewed
BOM lookup, on-hand check at correct location, warn if short components.
-
1.3
Production / assembly completed
Crew executes build on production floor. Components debited per BOM.
-
1.4
Assembly Build completed in NetSuite
NS AsmBuild record posted (
createdfrom = wo.id, quantity_built, waste). Mike approves at/assembly-build.html. -
1.5
FG added to inventory at correct location
inventory_balance.quantityavailable += quantity_builtat the WO's location_id. -
1.6
SO fulfilled to customer · Item Fulfillment
NS ItemShip posted with
createdfrom = so.id(NOT wo.id),otherrefnuminherits from SO. ★ Automation alert fires to Finance. -
1.7
Finance review · invoice · payment · close
Finance reviews fulfillment, posts CustInvc, monitors AR terms, payment received + applied, order closed.
Path 2 — Inventory build for stock
-
2.1
WO entered in NetSuite (standalone)
WO created with
createdfrom = NULL— no parent SO. Build reason captured (reorder_point / forecast / bid_pre_build / usda_drawdown / rd_pilot). -
2.2
Item, qty, location, component requirements confirmed
Operator confirms item, quantity, location, component requirements derived from
assembly_bom. -
2.3
Component availability reviewed
BOM lookup, on-hand check. If short, stage a purchase order (Path 2 has no customer pressure since no SO).
-
2.4
Production / assembly completed
Crew executes build for target qty. Components debited per BOM.
-
2.5
Assembly Build completed in NetSuite
NS AsmBuild record posted (
createdfrom = wo.id). Mike approves at/assembly-build.html. -
2.6
FG added to inventory at correct location
FG credited at correct location_id; allocatable to future SOs.
-
2.7
Components consumed · WO closes naturally
Formal component consumption posted (WkOrdIss). WO closes naturally (status='Built'). No customer side. No Finance handoff.
-
2.8
FG remains available for future SOs
Finished goods sit on the shelf at the correct location, allocatable to any future Sales Order. Finance enters when that future SO sells the FG.
What's different after the cycle
- Required production / assembly completed.
- Assembly Build entered in NetSuite.
- Finished goods added to inventory at the correct location.
- Components consumed properly.
- Any production issues, shortages, variances reviewed.
- WO closed in NetSuite (status='Built').
- Path 1 only: SO fulfilled, invoice posted, payment applied, order closed.
What can go wrong
Production completed the assembly build, finished goods are credited, but the SO still shows "Pending Fulfillment". The createdfrom link likely got dropped. Resync the WO; rebuild link manually if needed. See runbook scenario.
Item Fulfillment closed but Finance's review queue is empty. NS workflow trigger fired but delivery failed. Check NS workflow execution log, sync_log, ns_pending_pushes.
Assembly Build posted, but FG landed at wrong location_id (or no location captured). FG won't be allocatable to the expected location's SOs. Patch inventory_balance; investigate why the location field was missing.
Path 1 with short components can proceed and backorder downstream lines. Path 2 should stage a PO first (no customer pressure). Today operator decides; we don't have an auto-PO trigger.
Mike's "production issues, shortages, variances reviewed" gate. If waste > threshold, flag for review before closing the WO. Today qualitative; should be quantitative against BOM yield estimate.
Until Mike confirms the WO body field for customer PO#, the threading from SO.otherrefnum into the WO is unverifiable. Grep finds the PO# on SO, PO, Invoice, VendBill, IF — but the WO row is the missing link in the chain audit.
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| WO mirror table | work_orders, transactions WHERE type='WorkOrd' |
| Assembly Build table | assembly_builds (createdfrom = wo.id) |
| Path 1 link | workorder.createdfrom = transactions.id (SO id) |
| Path 2 standalone | workorder.createdfrom IS NULL |
| FG credit | inventory_balance.quantityavailable += quantity_built WHERE item_code=? AND location_id=? |
| HITL surface | /assembly-build.html → proposed_actions |
| Finance alert (Path 1, STUB) | events.wo.finance_alert_fired on itemfulfillment.status='Shipped' |
| Sync tier | hot (2 min) for WO + Assembly Build |
| STUB — WO PO# field | TBD — Mike review pending; expected bodyFields.memo |
| STUB — auto-classify origin | operator decides; no auto-classifier on /api/wo/create |
Dated trail · spot stale claims
Dated trail of when this doc was last touched, what changed, and what to look at if it feels stale.
| Date | Round | Change | Touched by |
|---|---|---|---|
2026-05-26 | R593 | Split into master + 2 path-detail docs matching SO pattern. Master is now the dispatcher; wo_lifecycle_from_sales_order_path and wo_lifecycle_inventory_build_path sub-contracts each have their own deep diagram + wiki. Renamed ns-work-order-lifecycle.html → ns-work-order-master.html (and the wiki). WO customer PO# field flagged TBD pending Mike's review (expected pattern bodyFields.memo). | Mike + Claude |
2026-05-25 | R586 | Added CHANGELOG · SCHEMA · RUNBOOK · BACKLOG sections — wiki became best-in-class operating documentation. | Mike + Claude |
2026-05-24 | R585 | Wiki originally shipped — 9-phase pipeline (intake / pull / start / labor / issue / build / close / variance / FG). Pre-split monolith. | Mike + Claude |
workflow_definitions WHERE workflow_type='work_order_lifecycle' before acting on these claims.The machine-readable spec
Canonical fields, table names, endpoint signatures. Master workflow_type · work_order_lifecycle · risk_level · 2. Sub-contracts: wo_lifecycle_from_sales_order_path (risk 3), wo_lifecycle_inventory_build_path (risk 2).
Customer PO# threading on WO — TBD pending Mike review
Mike's notes confirmed PO# threading on every other record in the SO chain. The WO body field is the only TBD. Expected pattern: bodyFields.memo (mirrors PO threading). Path 1 only; Path 2 has no customer PO# (no parent SO).
| NS record | Field carrying customer PO# | Sample value | Status |
|---|---|---|---|
| Sales Order (parent for Path 1) | bodyFields.otherrefnum (thread origin) | "72622" | REAL |
| Work Order (Path 1) | TBD — Mike review pending; expected pattern bodyFields.memo | "72622" (TBD) | TBD — Mike review pending. Expected: bodyFields.memo |
| Work Order (Path 2) | N/A — no parent SO, no customer PO#. memo = freeform operator note (e.g. "bid B5875 pre-build") | n/a | REAL (no threading) |
| Assembly Build (both paths) | createdfrom = wo.id — ties to WO; WO ↔ SO link (Path 1 only) | WO id | REAL |
| Item Fulfillment (Path 1) | otherrefnum inherits from SO via NS standard linkage | "72622" | REAL |
| Customer Invoice (Path 1) | bodyFields.otherrefnum | "72622" | REAL |
Trace recipe (Path 1): grep "72622" across transactions.otherrefnum + item_fulfillments.otherrefnum + customer_invoices.otherrefnum. The WO row is the missing link until Mike confirms which body field carries the threading. Action: Mike to confirm. Default placeholder: WO.memo = SO.otherrefnum.
WO origin classification (drives sub-contract dispatch)
| origin | Sub-contract | Linkage | Customer side? |
|---|---|---|---|
from_sales_order | wo_lifecycle_from_sales_order_path (risk 3) | workorder.createdfrom = so.id | Yes — SO fulfilled, Finance handoff, invoice, payment, close |
inventory_build | wo_lifecycle_inventory_build_path (risk 2) | workorder.createdfrom = NULL | No — FG to inventory, future SO may consume |
Inputs (required + optional)
| Field | Type | Description |
|---|---|---|
origin | enum | from_sales_order · inventory_build. Drives sub-contract dispatch. Required. |
assembly_item | string | NS assembly item_code. Required. |
quantity | integer | Target build qty. Required. |
location_id | integer | NS location id where FG will land. Required. |
so_id | integer | Parent SO id. Required for Path 1; NULL for Path 2. |
customer_po_number | string | TBD — Mike review pending. Expected: WO.bodyFields.memo on Path 1 (threaded from SO.otherrefnum). |
build_reason | enum | Path 2 only: reorder_point · forecast · bid_pre_build · usda_drawdown · rd_pilot · other |
D1 tables touched (across both paths)
| Table | Path | Operation |
|---|---|---|
work_orders / transactions (WorkOrd) | common | INSERT (Path 1 with createdfrom = so.id; Path 2 NULL) |
assembly_bom | common | READ (BOM lookup) |
inventory_balance | common | UPDATE: components -=, FG += |
assembly_builds / transactions (Build) | common | INSERT (createdfrom = wo.id) |
transactions (WkOrdIss) | common | INSERT per component consume |
item_fulfillments | Path 1 only | INSERT (createdfrom = so.id) |
customer_invoices | Path 1 only | INSERT (CustInvc; otherrefnum from SO) |
customer_payments · payment_applications | Path 1 only | INSERT on remit |
proposed_actions | common | INSERT for Assembly Build HITL gate |
events | common (Path 1 extra) | workorder.created, workorder.built, wo.finance_alert_fired (Path 1 STUB), order.closed (Path 1) |
Endpoints called
| Method | Path | Purpose |
|---|---|---|
POST | /api/wo/create | WO orchestration (today manual; auto-classify STUB) |
POST | /api/ns/workorder/release | flips status In Progress via NS_PUSH_QUEUE |
POST | /api/ns/workorder/close | finalizes WO; triggers variance audit |
GET | /api/assemblies/{id}/bom | resolves current BOM |
POST | /api/proposed-actions/bulk-decide | HITL approve Assembly Build |
Events fired
| event_type | When | Subscribers |
|---|---|---|
workorder.created | WO posted in NS, mirrored to D1 | production_admin, inventory |
workorder.built | WO closes (status=Built) after Assembly Build | inventory, customer_health (Path 1) |
assembly_build.posted | AsmBuild record posted | analytics, cost_rollup |
inventory.fg_credited | FG added to inventory_balance at location | SO_allocation_watcher, item_entity_page |
wo.finance_alert_fired | Path 1 only — Item Fulfillment closes (STUB pending NS workflow) | Finance |
order.closed | Path 1 only — SO closes after payment apply | customer_health, analytics |
It broke at 2am — what now
Different from "how do I use this." This is the page Mike pulls up when something is wrong: logs to check, recovery steps, who to escalate to.
Scenario · Path 1 Assembly Build completed but SO never fulfilled (WO/SO link broken)
Production finished the build, finished goods are credited, but the SO still shows "Pending Fulfillment". Path 1 stalled at step 1.6.
- Verify the link:
SELECT id, createdfrom, status FROM work_orders WHERE id=?— createdfrom should equal SO id - If createdfrom is NULL: patch with
UPDATE work_orders SET createdfrom=? WHERE id=?and force resync - Force SO resync:
POST /api/sync/run?tier=hot&table=transactions&id=<so_id> - Trigger fulfillment manually in NS: from the SO, hit "Fulfill" with the assembly already on hand
Scenario · Path 1 Finance never got the alert
Item Fulfillment closed but Finance's review queue is empty. The automation alert didn't deliver.
- Check NS workflow execution: NS workflow studio → wo_fulfillment_alert → execution log for this fulfillment
- Check event:
SELECT * FROM events WHERE event_type='wo.finance_alert_fired' AND entity_id=? ORDER BY created_at DESC - Check ns_pending_pushes:
SELECT * FROM ns_pending_pushes WHERE record_type='itemfulfillment_alert' ORDER BY created_at DESC LIMIT 10 - Fall back: manually notify Finance with the fulfillment ID; investigate NS workflow config afterwards
Scenario · Path 2 FG landed at wrong location
Assembly Build posted, but inventory_balance shows FG at a location the operator didn't intend. Future SO allocation will fail.
- Find the FG row:
SELECT * FROM inventory_balance WHERE item_code=? ORDER BY updated_at DESC LIMIT 5 - Check WO.location vs AsmBuild.location:
SELECT id, location FROM work_orders WHERE id=?andSELECT id, location FROM assembly_builds WHERE wo_id=? - If WO has correct location but AsmBuild doesn't: investigate NS AsmBuild record's location field; manually move inventory via location transfer
- NS location transfer: issue a transfer order from wrong-location to correct-location for the FG qty
Scenario · WO PO# field threading break (Path 1) — pending Mike
Auditing the SO chain for customer PO# "72622": SO shows it on otherrefnum, IF inherits, Invoice carries it — but the WO row is unverifiable because the body field is TBD.
- Confirm with Mike: which WO body field is intended to carry the customer PO#? Expected:
bodyFields.memo - Until confirmed: trace via
createdfrom = so.idlinkage only; this gets you from SO to WO without needing the PO# in a body field - Schema fix (post-confirmation): add verify_check to
wo_lifecycle_from_sales_order_path—WO.memo == SO.otherrefnum
Logs to check
workflow_run_log· top-level run audit (work_order_lifecycle and sub-contracts)workflow_step_log· per-step traceworkflow_verify_results· post-window verify outcomes (incl. WO PO# TBD check)events· workorder.created / .built / wo.finance_alert_fired trailproposed_actions· Assembly Build HITL gate statenpx wrangler tail· live Worker logs
Kill switch · emergency stop
kill:ns_writes· stops every NS push platform-widekill:proposed_apply· stops HITL approvals from executing fan-outkill:high_risk_ops· stops risk_level ≥ 4 fan-out
Escalation
Primary: Mike Levine (single-admin) · mikelevine@globalfoodsolutions.co. For production-floor outage during business hours, notify warehouse lead so dependent SOs can be re-planned.
What's not done · what's uncertain
What's not done, what's uncertain, what we punted. Captured so it survives context switches and doesn't die in someone's head.
-
TBD
WO customer PO# field — Mike review pending
Every WO Schema reference is flagged TBD until Mike confirms which body field carries the customer PO# threading on Path 1. Expected pattern:
bodyFields.memo. Once confirmed, add verify_checkWO.memo == SO.otherrefnumtowo_lifecycle_from_sales_order_path. -
STUB
Auto-classify WO origin (from_sales_order vs inventory_build)
Today operator decides. Classifier on
/api/wo/createcould auto-dispatch based on the presence of a parent SO assembly_line. -
STUB
NS workflow for Path 1 Finance alert
Platform fires
events.wo.finance_alert_firedtoday. The actual NS workflow trigger onitemfulfillment.status='Shipped'still needs to be built and verified in NS workflow studio. -
STUB
Labor capture autoflow
NS natively supports time-entries against WOs; we don't have an auto-capture surface (scanner / kiosk). Manual NS entry only today.
-
DECISION
Yield variance separate from cost variance
Today we audit cost variance at 5% threshold. Mike's "production issues, shortages, variances reviewed" gate suggests we should also audit qty produced vs qty planned. Open whether to add a second variance threshold or fold yield into cost.
-
OPEN
Path 2 auto-stage PO if components short
Today operator stages PO manually if Path 2 build hits component shortfall. Could auto-stage when
review_componentsverify_check returns warn. -
DEFER
Per-WO profitability snapshot
Capture margin at WO completion for Path 1 (FG cost vs SO line revenue). Useful for Mike to see assembly-line margin in real time, not just at month-end.