flowchart TD
subgraph S1["1. Intake · one door: bol@"]
A1["One door: bol@<br/>other inboxes forward in;<br/>future Suitelet posts into bol@"]:::yellow
A2["bol@ processing intake<br/>keeps BOL, Receiving, Shipping<br/>ignores spam and noise"]:::green
A3["Attachments saved to Studio<br/>hourly check, weekdays"]:::green
ALLOW{"PLANNED · Allowlisted sender?"}:::planned
A1 --> A2 --> ALLOW
ALLOW -->|"yes"| A3
SUB["PLANNED · Dock Portal 2542 Submit tab (PIN)<br/>pick open PO/SO · phone photos (sheet + vendor BOL) · note<br/>same intake as bol@ · PO pre-linked"]:::planned
SUB -->|"same intake · PO pre-linked · PIN = trusted sender"| A2
end
subgraph S2["2. Sort the ticket"]
B1{"Shipped or Received?"}:::green
B2["Clean up the order ref<br/>extra digits, vendor numbers"]:::green
B3{"One order per email?"}:::yellow
B1 -->|"PO = received · SO = shipped"| B2
B2 --> B3
end
subgraph S3R["3. Receiving sheet RS-v2.1 loop · at the dock, then checked · NEW Oct 5 · dashed = PLANNED"]
R1["PLANNED · 3.1 Print receiving sheet RS-v2.1<br/>at truck arrival · Dock Portal 2542 → Receiving<br/>2 QR codes + 4 corner markers"]:::planned
R2["PLANNED · 3.2 Dock fills 5 numbered steps<br/>black tab = must fill<br/>any STOP tick forces HOLD"]:::planned
R3["PLANNED · 3.3 Photo of sheet + vendor BOL<br/>emailed to bol@<br/>enters through step 1 intake (A1-A3)"]:::planned
R4["3.4 QR + version check<br/>ArUco + QR corners find the page<br/>tested 12/12 · not live"]:::yellow
R5["3.5 Reader + 2-engine agree<br/>qwen2.5vl VLM + Apple Vision<br/>94% auto · 0 wrong · synthetic"]:::yellow
R6["PLANNED · 3.6 Sheet vs vendor BOL cross-check<br/>seal · BOL # · carrier · counts vs PO (from QR)<br/>clean → step 4 · else ONE gap task"]:::planned
R7["3.7 Hold rules: 12 codes<br/>codes built in reader, lane rules planned<br/>H-BARCODE BLANK CONFLICT LOT COUNT SEAL<br/>TEMP SIG PHOTO CARRIER DECISION READ"]:::yellow
R8["PLANNED · 3.8 Gap filler, never invent<br/>PO › vendor BOL OCR › email/voice ›<br/>Label Studio template › prior receipt"]:::planned
R9["PLANNED · 3.9 Slack gap task<br/>one item per hold: vendor · PO · field<br/>hold code · owner · due · print ID"]:::planned
R10["PLANNED · 3.10 Write-back: 3 doors, same IR fields<br/>re-photo · Dock Portal 2542 tablet twin · Slack reply<br/>audit trail · re-run all rules"]:::planned
R1 --> R2 --> R3
R4 --> R5 --> R6
R4 -->|"H-BARCODE"| R7
R5 -->|"H-READ"| R7
REJ(["PLANNED · Rejected: load refused<br/>no IR · FS logged (2971)"]):::planned
R7 -->|"REJECT decision"| REJ
R6 -->|"any gap"| R7
R7 -->|"try to fill"| R8
R8 -->|"still blank"| R9
R9 -->|"owner answers"| R10
R10 -->|"re-run every rule"| R6
end
subgraph S3["4. Read the paper"]
C1["Prefer PDF or clean scan<br/>over phone photo"]:::yellow
C2["3 scanners read the BOL<br/>received: vendor BOL (sheet read in 3.5)<br/>shipped: our BOL + inspection sheet<br/>ref, SKU, qty, lot, expiry, temp"]:::yellow
C3{"Do the readings agree?"}:::yellow
C1 --> C2 --> C3
end
subgraph S4["5. Match in NetSuite"]
D1["Find the PO if received<br/>or the SO if shipped"]:::green
D2{"Heartland 4 or Quaker 18?"}:::green
D3{"Qty matches the order?<br/>tolerance rule not set yet"}:::yellow
D1 --> D2
D2 -->|"Heartland 4 or Quaker 18 → continue"| D3
end
subgraph S5["6. Duplicate check"]
E1{"Receipt or fulfillment exists,<br/>or same BOL # already posted?"}:::green
E2["Compare only<br/>never create a duplicate"]:::green
E3["Build the draft<br/>with lots and qty"]:::green
E1 -->|"yes"| E2
E1 -->|"no"| E3
CLOSE(["Compare-ok · close<br/>no new Save"]):::green
E2 -->|"matches"| CLOSE
end
subgraph S6["7. Save in NetSuite"]
F1["Item Receipt if received"]:::locked
F2["Item Fulfillment if shipped<br/>ship status still held"]:::locked
F3["Read it back to confirm<br/>the Save really worked"]:::locked
F1 --> F3
R11["7 · Item Receipt with FS fields<br/>seal · temp · checks · BOL # · lots · decision<br/>fields 0% filled today · Save LOCKED"]:::locked
F1 -.->|"received: carries the sheet fields"| R11
F2 --> F3
OUT["PLANNED · Outbound check + FS fields on IF<br/>temp · seal · trailer · lots<br/>sheet is receiving-only"]:::planned
F2 -.->|"shipped: outbound checks"| OUT
end
subgraph S7["8. Dock log"]
G1["Dock log: lane writes IR/IF dock fields<br/>plus Food Safety Log entry (2971)<br/>2970 retiring, replaced by lane"]:::locked
end
subgraph S8["9. Tell the right people"]
H1["DRAFT · Done reply to whoever sent the BOL"]:::locked
H2["DRAFT · Email food safety@<br/>(address unconfirmed)<br/>lots, expiry, temp, inspection,<br/>log entry"]:::newstep
H3["DRAFT · Email Amanda, AP<br/>received: match vendor bill<br/>to the receipt"]:::newstep
R12["PLANNED · 9 · Bill / Invoice copy-forward<br/>UE script: IR→Bill, IF→Invoice<br/>BOL # · carrier · trailer · seal"]:::planned
H4["DRAFT · Email Amanda, AR<br/>shipped: draft email today<br/>(auto invoice is future / pending)"]:::newstep
end
subgraph S8b["10. After verified shipped fulfill (PENDING · not built)"]
H5["PENDING · Auto invoice customer (AR)<br/>NOT BUILT — today Amanda gets a draft email only"]:::red
H6["PENDING · If backorder, auto-close it<br/>NOT BUILT"]:::red
H7["PENDING · Post a note to a Slack channel<br/>NOT BUILT — channel not chosen"]:::red
H5 --> H6 --> H7
end
subgraph S8c["11. Collections (PENDING · not built)"]
H8["PENDING · Collections with terms applied<br/>NOT BUILT"]:::red
H9["PENDING · Dunning brackets (days past due)<br/>3, 5, 7, 10, 14, 21, 30,<br/>45, 60, 75, 90, 120<br/>NOT BUILT"]:::red
H10["PENDING · Dunning letters +<br/>automated collections process<br/>NOT BUILT"]:::red
H8 --> H9 --> H10
end
subgraph S9["Score · feedback · training"]
SCORE["Score against gold / holdout<br/>public scores = engine-agree today"]:::yellow
FB["Feedback from held gaps<br/>lot / expiry stay open"]:::yellow
TRAIN["Training from tagged pages<br/>holdout scores the extractor"]:::yellow
R13["PLANNED · Learning loop<br/>closed gaps + gold tags + new-vendor template<br/>20-page holdout gate"]:::planned
GO["PLANNED · Go-live path<br/>1 sheet prints live, bot = zero-Save shadow<br/>2 unlock after clean weeks + holdout + bol@ allowlist"]:::planned
CL["Claude post gated by score<br/>shadow until prove unlock"]:::red
end
subgraph S12["12. NetSuite fields + build list · NEW Oct 5-6"]
AUD["NS dock field audit Oct 5, read-only<br/>24 FS fields + lot/expiry = 0% filled<br/>no BOL # field · carrier only from PO/SO"]:::green
CC["PLANNED · Claude Code build list Oct 6<br/>BOL # field · received-by · copy-forward UE<br/>RS-v2.1 in recvXml · lane FS/lot writes · retire 2970 · dup BOL"]:::planned
AUD -.->|"feeds"| CC
end
subgraph S13["13. Oct 7, 2026 changes · green = LIVE · dashed red = PENDING"]
subgraph S13A["13a. Dock Portal v1.3 · LIVE"]
V13["LIVE Oct 7 · Dock Portal v1.3<br/>shared 60 s board cache<br/>no embedded fonts → pages ~half size"]:::green
end
subgraph S13B["13b. v1.4 share sheet · LIVE"]
V14["LIVE Oct 7 · v1.4 tap BOL / Receiving → sheet<br/>Open / Abrir · Print / Imprimir<br/>Text it / Enviar por texto"]:::green
LNK["LIVE Oct 7 · Texted link<br/>opens ONE document only · signed with PIN hash<br/>stops at midnight New York time"]:::green
KILL["LIVE Oct 7 · Kill switch<br/>SHARE_LINKS=0 turns texted links off"]:::green
end
subgraph S13C["13c. v1.4.1 fixes · LIVE"]
PAL["LIVE Oct 7 · v1.4.1 · Past Due pallet math<br/>uses the line's cases-per-pallet<br/>freight lines excluded"]:::green
DBL["LIVE Oct 7 · v1.4.1 · Double tap on Text it<br/>starts only ONE share"]:::green
STALE["LIVE Oct 7 · v1.4.1 · Stale link<br/>tells the crew to refresh"]:::green
ERR["LIVE Oct 7 · v1.4.1 · Error pages<br/>generic message on the public side"]:::green
POP["LIVE Oct 7 · v1.4.1 · Blocked pop-up<br/>shows Allow pop-ups notice"]:::green
end
subgraph S13D["13d. Receiving sheet RS-v2.2 · LIVE (reader template pending)"]
RS1["LIVE Oct 7 · RS-v2.2 sheet<br/>full item name + description<br/>taller rows"]:::green
RS2["LIVE Oct 7 · QR tag RS-V2.2"]:::green
RS3["LIVE Oct 7 · reader routes by QR tag<br/>PENDING: rs-v2.2 template needs a real reprint"]:::yellow
RS4["LIVE Oct 7 · Units fix<br/>blank units print as 'units'<br/>non-case units totalled separately"]:::green
end
subgraph S13E["13e. BOL print fixes · LIVE"]
BL1["LIVE Oct 7 · BOL fix<br/>one page for long orders"]:::green
BL2["LIVE Oct 7 · BOL fix<br/>clean totals cell"]:::green
BL3["LIVE Oct 7 · BOL fix<br/>a dash ( – ) instead of a false 0"]:::green
BL4["LIVE Oct 7 · BOL fix<br/>MIXED LOAD temperature text"]:::green
end
subgraph S13F["13f. Security role 1266 · created, NOT switched on"]
SEC0["DONE · GFS Dock Portal Runner role<br/>id 1266 created<br/>NOT yet switched on deploy 10593"]:::green
SEC1["PENDING · Subsidiaries = All"]:::planned
SEC2["PENDING · Other Addresses → View"]:::planned
SEC3["PENDING · Baseline"]:::planned
SEC4["PENDING · Switch deployment 10593 to role 1266"]:::planned
SEC5["PENDING · Phone tests T1–T8"]:::planned
end
subgraph S13G["13g. Pending items · dashed = PENDING"]
PD1["PENDING · PO12993 reprint for reader template"]:::planned
PD2["PENDING · Real SO14501 BOL print"]:::planned
PD3["PENDING · TEST truck submit"]:::planned
PD4["PENDING · Dock Portal units patch (not deployed)"]:::planned
DC1["PENDING · Data: 12 SOs with blank ship-from<br/>(incl. SO13952)"]:::planned
DC2["PENDING · Data: 71 lines with no unit"]:::planned
DC3["PENDING · Data: TT10401 classed Frozen"]:::planned
DC4["PENDING · Data: garbled ½ text"]:::planned
end
V14 -->|"Text it"| LNK
LNK -.->|"off switch"| KILL
V14 --> DBL
LNK -->|"expired / old"| STALE
V14 -->|"Open / Print blocked"| POP
LNK -->|"error"| ERR
RS2 -->|"tag read"| RS3
RS3 -.->|"needs real sheet"| PD1
RS4 -.->|"portal side"| PD4
BL1 -.->|"confirm on paper"| PD2
SEC0 --> SEC1 --> SEC2 --> SEC3 --> SEC4 --> SEC5
PAL -.->|"needs units"| DC2
end
R1 -.->|"Oct 7: now prints RS-v2.2"| RS1
R4 -.->|"Oct 7: routes by tag"| RS3
R1 -.->|"Oct 7: tap Receiving → share sheet"| V14
V13 ~~~ V14
N["Sent to Michael<br/>with the reason"]:::you
A3 --> B1
ALLOW -->|"no · unknown sender"| N
B1 -->|"transfer / unclear"| N
A1 <-.-|"this email"| R3
B1 <-.-|"resolved → back into lane"| N
B3 -->|"yes · received with sheet"| R4
B3 -->|"yes · shipped / no sheet<br/>after go-live: received with no sheet → hold"| C1
R6 -->|"clean"| C1
R9 -.->|"no owner closes it"| N
B3 -->|"slashed refs"| N
C3 -->|"yes"| D1
C3 -->|"no"| N
D2 -->|"loc 17 / other loc / transfer"| N
D3 -->|"off"| N
D3 -->|"ok"| E1
E2 -->|"differences"| N
E3 -->|"received"| F1
E3 -->|"shipped"| F2
F3 --> G1
G1 --> H1
G1 --> H2
G1 -->|"received"| H3
G1 -->|"shipped"| H4
H4 -->|"after verified IF · future"| H5
H5 -->|"after invoice · future"| H8
M["Monday history<br/>713 folders"]:::yellow -.->|"trains"| C2
L["Gold tagged pages<br/>early — Label Studio"]:::yellow
X["Multica takeover<br/>Claude runtime; NS not proven yet"]:::red -.->|"runs steps 5 to 9 later"| D1
C3 -.->|"agree / disagree"| SCORE
L -.->|"gold target"| SCORE
L -.->|"tagged pages"| TRAIN
TRAIN -.->|"trains extractor"| C2
SCORE -.->|"fail → needs-you"| N
SCORE -.->|"gate"| CL
CL -.->|"unlocks first Save"| F1
CL -.->|"unlocks first Save"| F2
N -.->|"held gaps"| FB
FB -.->|"close → write back"| SCORE
FB -.->|"closed sheet gaps"| R13
R13 -.->|"holdout gate"| GO
GO -.->|"same gate"| CL
H3 -.->|"planned"| R12
H4 -.->|"planned"| R12
H10 ~~~ AUD
classDef green fill:#c8f7c5,stroke:#2e7d32,color:#000
classDef yellow fill:#fff3b0,stroke:#b8860b,color:#000
classDef red fill:#ffc9c9,stroke:#c62828,color:#000
classDef locked fill:#e0e0e0,stroke:#616161,color:#000
classDef you fill:#cfe2ff,stroke:#1565c0,color:#000
classDef newstep fill:#e1c9ff,stroke:#6a1b9a,color:#000
classDef planned fill:#fff5f5,stroke:#c62828,stroke-width:2px,stroke-dasharray:6 4,color:#000
click A1 call showDetail("A1")
click A2 call showDetail("A2")
click A3 call showDetail("A3")
click B1 call showDetail("B1")
click B2 call showDetail("B2")
click B3 call showDetail("B3")
click C1 call showDetail("C1")
click C2 call showDetail("C2")
click C3 call showDetail("C3")
click D1 call showDetail("D1")
click D2 call showDetail("D2")
click D3 call showDetail("D3")
click E1 call showDetail("E1")
click E2 call showDetail("E2")
click E3 call showDetail("E3")
click F1 call showDetail("F1")
click F2 call showDetail("F2")
click F3 call showDetail("F3")
click G1 call showDetail("G1")
click H1 call showDetail("H1")
click H2 call showDetail("H2")
click H3 call showDetail("H3")
click H4 call showDetail("H4")
click H5 call showDetail("H5")
click H6 call showDetail("H6")
click H7 call showDetail("H7")
click H8 call showDetail("H8")
click H9 call showDetail("H9")
click H10 call showDetail("H10")
click N call showDetail("N")
click M call showDetail("M")
click L call showDetail("L")
click X call showDetail("X")
click SCORE call showDetail("SCORE")
click FB call showDetail("FB")
click TRAIN call showDetail("TRAIN")
click CL call showDetail("CL")
click R1 call showDetail("R1")
click R2 call showDetail("R2")
click R3 call showDetail("R3")
click R4 call showDetail("R4")
click R5 call showDetail("R5")
click R6 call showDetail("R6")
click R7 call showDetail("R7")
click R8 call showDetail("R8")
click R9 call showDetail("R9")
click R10 call showDetail("R10")
click R11 call showDetail("R11")
click R12 call showDetail("R12")
click R13 call showDetail("R13")
click GO call showDetail("GO")
click AUD call showDetail("AUD")
click CC call showDetail("CC")
click ALLOW call showDetail("ALLOW")
click CLOSE call showDetail("CLOSE")
click REJ call showDetail("REJ")
click OUT call showDetail("OUT")
click SUB call showDetail("SUB")
click V13 call showDetail("V13")
click V14 call showDetail("V14")
click LNK call showDetail("LNK")
click KILL call showDetail("KILL")
click PAL call showDetail("PAL")
click DBL call showDetail("DBL")
click STALE call showDetail("STALE")
click ERR call showDetail("ERR")
click POP call showDetail("POP")
click RS1 call showDetail("RS1")
click RS2 call showDetail("RS2")
click RS3 call showDetail("RS3")
click RS4 call showDetail("RS4")
click BL1 call showDetail("BL1")
click BL2 call showDetail("BL2")
click BL3 call showDetail("BL3")
click BL4 call showDetail("BL4")
click SEC0 call showDetail("SEC0")
click SEC1 call showDetail("SEC1")
click SEC2 call showDetail("SEC2")
click SEC3 call showDetail("SEC3")
click SEC4 call showDetail("SEC4")
click SEC5 call showDetail("SEC5")
click PD1 call showDetail("PD1")
click PD2 call showDetail("PD2")
click PD3 call showDetail("PD3")
click PD4 call showDetail("PD4")
click DC1 call showDetail("DC1")
click DC2 call showDetail("DC2")
click DC3 call showDetail("DC3")
click DC4 call showDetail("DC4")
Click any box to open its notes (what it does, where it runs, engine/model, data, numbers, next step). Tip: use Large or + to make the boxes bigger, then scroll. Dashed arrows are the score / feedback / training loop and later runners. Every arrow into the blue box means "stop and send it to Michael with the reason."
What each step means
1Intake doors
Own facilities (Heartland 4 / Quaker 18): Suitelet side tab is the locked team front door (drop BOL + short note → bol@ lane behind the screen). NOT built — Mike recodes. Email to bol@ / receiving@ / shipping@ is backup. Drop ship keeps its own door. Bots Mail still runs the bol@ intake under every door (hourly weekday check).
Target locked · Suitelet not built2Sort the ticket
The bot decides whether it was shipped or received and cleans up the order number (extra digits, vendor numbers). If one email covers several orders ("slashed refs"), it goes to Michael.
Mostly works multi-order is weak3Receiving sheet loop (new Oct 5)
At the dock: when the truck arrives, print the RS-v2.1 sheet with 2 QR codes (Dock Portal 2542) and fill its 5 numbered steps (black tab = must fill, STOP forces HOLD). Then photo the sheet + vendor BOL to bol@, which enters through step 1. In the lane, after sorting: QR check → reader with 2 engines that must agree → sheet vs BOL cross-check → 12 hold codes → gap filler (never invents) → one Slack gap task → write-back (photo / Dock Portal 2542 tablet twin / Slack), then re-check. Clean tickets go on to step 4. Stress test: QR 12/12, ticks 61/61, 94% auto-accepted with 0 wrong on synthetic photos.
reader tested mostly planned4Read the paper
Three scanners read the ref, SKU, qty, lot, expiry, temperature and inspection sheet. A PDF or clean scan beats a phone photo; if the three readings don't agree, it goes to Michael.
Works but weak — the weak link5Match in NetSuite
Find the PO (received) or SO (shipped) and confirm it's Heartland (loc 4) or Quaker (loc 18). Loc 17, transfers, or a qty that's off go to Michael; the qty tolerance rule is not set yet.
Works today qty rule pending6Duplicate check
If a receipt or fulfillment already exists, the bot only compares and never creates a duplicate — differences go to Michael. Otherwise it builds the draft with lots and qty.
Works today7Save in NetSuite
Creates the Item Receipt (received) or Item Fulfillment (shipped, ship status still held), then reads it back to confirm the Save really worked.
Built but locked8Dock log
Adds the Dock Fulfillment and Receiving entry plus the Food Safety Log entry.
Built but locked9Tell the right people
A "done" reply to whoever sent the BOL, plus new emails to food safety@ (lots, expiry, temp, inspection) and to Amanda — AP to match the vendor bill on receipts, AR draft email on shipments (auto invoice is a separate pending step).
New Sep 24 locked10After verified shipped fulfill (pending)
Shipped lane only, after a verified Item Fulfillment — three future steps, NOT BUILT: auto invoice the customer (AR) instead of only Amanda's draft email; if there is a backorder, auto-close it; post a note to a Slack channel (channel not chosen). Time not timed yet. Zero NetSuite Saves until prove unlock. Do not treat these as live.
Pending · not built11Collections (pending)
After invoice (future) — NOT BUILT: collections with terms applied; dunning brackets at 3, 5, 7, 10, 14, 21, 30, 45, 60, 75, 90, and 120 days past due; plus dunning letters and the automated collections process. Letter copy, fees, and any Slack channel are not invented here. Time not timed yet. Zero NetSuite Saves. Not live.
Pending · not built12NetSuite fields + build list
Oct 5 read-only audit: all 24 food-safety fields + lot/expiry are 0% filled on every IR/IF ever; carrier 23-29%, only copied from the PO/SO; no BOL # field. Build list for Claude Code: BOL # field, received-by, bol_attached on IR, copy-forward script, RS-v2.1 in recvXml(), FS/lot writes from the lane (2970 retiring, replaced by the lane; 2971 FS log stays), dup BOL guard. Deploy only after Michael OKs it.
audit done build list planned3. Receiving sheet RS-v2.1 loop (new Oct 5)
recvXml() in lib_gfs_bol.js (file 802283) was redesigned as RS-v2.1: 5 numbered steps, black must-fill tabs, STOP forces HOLD, two QR codes (payload RS-V2.1:TRANID:id:printid), and ArUco corners. A photo reader was built and stress-tested on 12 synthetic phone photos. Every step below is also a box in the diagram (click it for the same notes). Dashed red = PLANNED (not built). Nothing has been written to NetSuite.

3.1Print the receiving sheet (RS-v2.1, two QR codes)PLANNED · not built
What it checks
One Receiving Sheet per open PO, printed from the GFS Dock Portal Suitelet (customscript_gfs_dock_portal, script 2542) with the 'Receiving' button on each open inbound PO card (?action=print&doc=recv&id=<PO internal id>). The sheet is BFO XML built by recvXml() in lib_gfs_bol.js (File Cabinet file 802283). That is the one generator; no FreeMarker copy. RS-v2.1 is an upgrade of that existing sheet. It pre-prints the PO #, vendor, carrier, scheduled time and up to 8 expected lines (lines 9+ go on continuation pages). It also prints two QR codes, 38pt in the header and 26pt in the footer, using NetSuite BFO's native barcode tag. QR payload: RS-V2.1:<TRANID>:<PO internal id>:<print id yyMMddHHmmss>, in alphanumeric mode (version 2, 25x25 modules). ArUco corner markers 10/11/12/13 sit at the corners. The footer shows the print time and print ID so stale or duplicate copies can be caught.
Time
seconds (print)
Pass looks like
An open PO with qty remaining prints. Both QR codes and all 4 corner markers are on the page. The print ID is the newest one for this PO.
Hold looks like
Closed PO or nothing left to receive: the portal refuses to print. Older prints (RS-v2 with Code128, or the pre-v2 sheet) get H-BARCODE later and must be reprinted.
Runs on
NetSuite: Dock Portal Suitelet 2542 (PIN deployment customdeploy_gfs_dock_portal + internal customdeploy_gfs_dock_portal_int).
Engine or model
SuiteScript recvXml() → render.xmlToPdf (BFO PDF).
Where data lives
Layout: /workspace/bol-shadow/forms/receiving-sheet-v21.xml (builder build/build_receiving_sheet_v21.py). Live source copy: forms/existing-receiving-sheet/lib_gfs_bol_802283.js (recvXml ~lines 640-690).
Live or not / numbers
The existing (older) receiving sheet prints live from the portal today. RS-v2.1 is designed and stress-tested in preview only. It is NOT ported into recvXml(), and nothing has been written to NetSuite. Still to verify on one real BFO print: the QR is 25x25 (alphanumeric mode), and record.id and .now render.
Next step
Port RS-v2.1 into recvXml() (Claude Code build list, CC). Deploy only after Michael OKs it.
3.2Dock fills the 5 numbered stepsPLANNED · not built
What it checks
Step 1 TRUCK: seal #, trailer temp °F with an ABOVE 0 / BELOW 0 tick, then Y/N for seal OK (not cut), reefer running, clean / no bad smell, no bugs or pests, and load not crushed or wet. Step 2 PAPERWORK: BOL #, seal # on the BOL the same?, case + allergen labels OK?, and truck company = the printed carrier? If not, tick a carrier or write OTHER + name. Step 3 LINES: SAME or CASES GOT, SHORT / DAMAGED, LOT code, and EXPIRY MM DD YY on every line. Step 4 DECISION: ACCEPT / HOLD / REJECT plus STOP ticks (seal / temp / dirty / label / none). Step 5 SIGN-OFF: initials, receiver signature, date + 24h time, driver signature, photo ticks (all pages / BOL / seal) and notes. Every required box has a black corner tab, so it must be filled. Any STOP tick forces HOLD.
Time
a few minutes at the truck
Pass looks like
Every must-fill box is filled, exactly one decision is ticked, STOP = NONE (or HOLD is chosen), and both signatures are there.
Hold looks like
A blank must-fill box gives H-BLANK. A STOP tick with ACCEPT gives H-CONFLICT. HOLD or REJECT gives H-DECISION, the normal supervisor queue.
Runs on
Paper at the dock (Heartland loc 4 / Quaker loc 18).
Engine or model
People only. Temp, seal OK, the truck checks, counts, the decision and signatures are human-only fields that the bot never fills.
Where data lives
Paper → photo. Box-to-field map: forms/RECEIVING-SHEET-FIELD-MAP.md section B.
Live or not / numbers
PLANNED. The design is locked in preview (RS-v2.1), but the sheet is not printed at the dock yet.
Next step
Add 'No flash / no glare. Whole sheet, all 4 corner squares in frame.' to the sheet and the bol@ email template. Glare is the only failure that wasn't fixed.
3.3Photo of the sheet + vendor BOL emailed to bol@PLANNED · not built
What it checks
The dock photographs every page of the filled sheet, the vendor BOL and the seal, then emails them to bol@. That is the same intake used today. The photo enters through step 1 (boxes A1-A3) like every other ticket, with no separate door: Bots Mail Bot polls IMAP, saves the attachments to Studio, and the hourly weekday check picks them up. Step 2 sorts it as received, and the lane comes back to box 3.4.
Time
target: a few minutes, part of the clean path
Pass looks like
The email has the sheet photo(s), the vendor BOL and a seal photo.
Hold looks like
Missing attachments give H-PHOTO (lane check). An unknown sender is held (allowlist NOT LIVE).
Runs on
Zoho bol@ → Bots Mail Bot (IMAP poll) → Studio attachments/ (hourly, weekdays 9:47 AM-5:47 PM ET).
Engine or model
Bots Mail Bot (Grok).
Where data lives
Studio: Documents/GFS-bots-mailbox/attachments/YYYY-MM-DD-uidNNNN/.
Live or not / numbers
Email intake is live today. Sending the RS-v2.1 sheet photo with it is PLANNED, because the sheet isn't printing yet.
Next step
Update the bol@ email template with the photo rules once the sheet prints.
Oct 6 review fix
Oct 6 review: drawn as a dashed 'this email' link into A1. The photo enters through step 1 and is sorted in step 2 like every ticket; only B3 → 3.4 enters the check loop. Planned second entrance: the same photos can be sent from the Dock Portal 2542 'Submit' tab (box SUB), which posts into the same intake with the PO pre-linked.
3.4QR + version checkBuilt / tested · not live
What it checks
The reader finds the page from the ArUco markers plus the QR corners (RANSAC homography; any 2 of the 6 objects are enough). It decodes both QR codes: raw photo first, then the rectified page, then crops. The version must be RS-V2.1, and the payload gives TRANID, PO internal id and print ID. The lane then checks the PO is open, the print ID is the newest for that PO, no earlier sheet for the PO has posted, the NS lines haven't changed since the print, and all pages are present.
Time
under 1 s (QR + geometry)
Pass looks like
QR decoded, version known, tranid and id agree with NetSuite, newest print.
Hold looks like
Neither QR decoded, an unknown version, two different payloads, or an old RS-v2 print → H-BARCODE (reprint). Corners not found or layout doesn't match → H-READ.
Runs on
Planned on Mac Studio: reader/read_sheet.py. The Studio venv (~/.venvs/rs-reader) install is pending Michael's OK.
Engine or model
OpenCV ArUco + zxing-cpp.
Where data lives
reader/rs-v2.1/geom.json (slot geometry), reader/rs-v2.1/blank_300.png.
Live or not / numbers
Reader built and tested on 12 synthetic phone photos: QR 12/12 (all straight from the raw photo), corners 12/12 (up from 11/12 on RS-v2). Lane-side NetSuite checks (newest print, lines unchanged) are PLANNED.
Next step
Install the Studio venv after Michael OKs it, then run one real BFO print through the reader.
3.5Reader + 2-engine agreeBuilt / tested · not live
What it checks
read_sheet.py reads all 61 tick boxes by pixel ink and checks that the signature lines have ink. It reads the handwritten boxes with qwen2.5vl:7b (a VLM on local ollama) and with Apple Vision. A value is auto-accepted only when both engines agree and the VLM's character count equals the number of inked cells (digit fields must be all digits). Blank boxes are decided by ink and never sent to the VLM, because it makes up values for empty strips. Output is JSON: version, po, fields, lines[], ticks, decision, holds[], lane_checks[]. It feeds the hourly bol@ check.
Time
about 30 s per sheet on Studio
Pass looks like
Both engines agree and the confidence check passes, so the value is accepted.
Hold looks like
Engines disagree, the length or charset check fails, or a tick is ambiguous → H-READ. The dock lead confirms the value from the review crop.
Runs on
Mac Studio (ollama started only for the batch). Studio venv install pending Michael's OK.
Engine or model
qwen2.5vl:7b via ollama + Apple Vision (VNRecognizeTextRequest, swift). Tesseract is a fallback only: 20% of fields exact, so with it nearly everything ends up H-READ.
Where data lives
reader/read_sheet.py → JSON. Stress kit + results: forms/stress/, forms/SCAN-STRESS-TEST.md, scan-stress-contact-sheet.png.
Live or not / numbers
Stress test on Oct 5, 12 synthetic phone photos: ticks 61/61 on 12/12 sheets. 337 of 360 values auto-accepted (94%), 0 silently wrong, 23 held for a person. VLM and Apple Vision agreed on 259 fields with 0 wrong. Glare is the one failure that wasn't fixed. CAVEAT: synthetic handwriting font, a Chromium render rather than the real BFO PDF, and generated distortions. It must be validated on 20+ real dock photos before anything auto-posts.
Next step
Run 20+ real dock photos through it. Freeze those results as part of the holdout gate (R13).
3.6Sheet vs vendor BOL cross-checkPLANNED · not built
What it checks
The lane compares the sheet with the vendor BOL OCR and the NetSuite PO. Checks: the written seal # against the seal read off the BOL / seal photo, BOL # against the BOL, carrier against customlist407, counts against PO expected, initials against the employee lookup, all attachments present. (The dup-BOL guard now sits in step 6, box E1.) The vendor BOL values come from the same step 1 attachments; the full 3-scanner read of the BOL is step 4. If everything is clean, the ticket continues to step 4 (read the paper) and step 5 (match). It posts only in step 7, once Saves are unlocked. Otherwise it raises exactly one gap task for that sheet.
Time
target: a few minutes, part of the clean path
Pass looks like
Sheet, BOL and PO agree → continue to step 4 / 5 (Save in step 7 still locked).
Hold looks like
Seal mismatch → H-SEAL. Carrier with no list match → H-CARRIER. Over, or short/damaged not ticked → H-COUNT. Unknown initials → H-SIG. BOL # already used → dup_bol hold.
Runs on
Planned: BOL lane on Studio (shadow).
Engine or model
Rules + read-only NetSuite (user-netsuite MCP).
Where data lives
Field map section C; reader lane_checks[].
Live or not / numbers
PLANNED. The rules are defined (field map section C + reader lane_checks[]), but the lane code isn't built. Zero NetSuite writes.
Next step
Build it into the shadow lane after the reader is installed on Studio.
Oct 6 review fix
Oct 6 review: the PO comes from the sheet's QR (RS-V2.1:TRANID:id:printid → PO internal id), so counts are checked against that PO here and step 5 only confirms it. The duplicate-BOL guard moved to step 6 (E1) so there is one duplicate check, not two.
3.7Hold rules (12 codes, including the new H-READ)Built / tested · not live
What it checks
A sheet posts only when EVERY rule passes. H-BARCODE: no QR or an unknown version. H-BLANK: a must-fill box is still blank after the gap filler, or a Y/N has both or neither ticked. H-CONFLICT: STOP with ACCEPT, not exactly one decision, or SAME plus a count. H-LOT: lot or expiry blank, an invalid date, or expiry before the receipt date. H-COUNT: count ≠ expected with no SHORT/DAMAGED, or over. H-SEAL: seal not OK, seal-match NO, or the seal # differs from the BOL. H-TEMP: ACCEPT with Frozen > 0 °F or Chilled > 41 °F (PCQI limits unconfirmed). H-SIG: a signature is missing or the initials aren't found. H-PHOTO: missing photos. H-CARRIER: carrier NO with none ticked, or no list match. H-DECISION: HOLD or REJECT, which is the normal supervisor queue. H-READ (new, proposed): the reader can't place the page, a tick is ambiguous, or a handwritten field fails the confidence check.
Pass looks like
No hold codes → can post (after unlock).
Hold looks like
Any code → the IR doesn't post. Each code has an owner role and a due time (same shift / 24 h).
Runs on
Image-decidable rules: reader/read_sheet.py. NetSuite/email rules: lane (planned).
Engine or model
Rules.
Where data lives
forms/RECEIVING-SHEET-FIELD-MAP.md sections C + RS-v2.1 addendum; reader/README.md.
Live or not / numbers
Image rules are built in the reader (blank-sheet test: 12/12 held, 0 false ticks). The NetSuite/email rules are PLANNED. H-READ still needs to be added to the field map officially.
Next step
Confirm the ACCEPT/HOLD/REJECT → disposition/status mapping and the temp limits with PCQI.
Oct 6 review fix
Oct 6 review: the 12 codes are built into the reader; the lane-side hold rules are planned. QR/version failures (H-BARCODE, from 3.4) and engine disagreement (H-READ, from 3.5) now route here. A REJECT decision ends at the Rejected box (load refused, no IR, FS logged).
3.8Gap filler (fills only what's safe; never invents)PLANNED · not built
What it checks
A blank must-fill box may be filled only from these sources, in this order: 1. NetSuite PO/SO data; 2. vendor BOL OCR; 3. the email body, a voice note or email metadata; 4. the Label Studio vendor template (case-label OCR); 5. a prior receipt from the same vendor (flagged). It never invents a value. Every auto-filled value is tagged with its source on the IR (exceptions text) for audit. HUMAN-ONLY, never auto-filled: temp, seal OK, the truck checks, counts (never copied from EXPECT), seal-match, labels OK, STOP + decision, and signatures.
Pass looks like
The blank is filled from an allowed source and the value is tagged with that source.
Hold looks like
Nothing allowed can fill it → the hold stays and one Slack gap task is created (R9).
Runs on
Planned: BOL lane (shadow).
Engine or model
Rules + OCR outputs already on Studio.
Where data lives
Field map section D (slot-by-slot sources and owners).
Live or not / numbers
Design only, NOT built.
Next step
Build it after R6. Uses the Label Studio vendor templates from the learning loop.
3.9Slack gap taskPLANNED · not built
What it checks
One Slack List item per hold, with columns for vendor, PO, missing field, hold code, owner role (dock lead / receiving supervisor / PCQI / purchasing / ops admin), due (same shift or 24 h) and print ID. The owner replies in the item's thread with the value.
Pass looks like
The owner replies with a valid value → write-back (3.10).
Hold looks like
No reply by the due time → stays open, the IR doesn't post, and it goes to Michael (blue box) with the reason.
Runs on
Planned: Slack List.
Where data lives
Slack List (not created).
Live or not / numbers
Design only. The Slack List is NOT BUILT, and the channel and List aren't chosen. Nothing has been created.
Next step
Michael picks the channel/List. Then build it.
3.10Write-back (three correction doors, one set of IR fields)PLANNED · not built
What it checks
Corrections can arrive three ways: a re-photo of the sheet, a tablet/iPad twin of the paper sheet on the Dock Portal (2542, the dock team's live PIN screen; the twin is planned and submits into the lane), or a Slack reply in the gap task. All three are validated against the same rules and write the SAME Item Receipt fields, with an audit trail (source tag + who/when). The lane then re-runs every rule (back to 3.6), and the IR posts only when no hold remains. Closed gaps also feed the learning loop. This is the dock-sheet version of the existing feedback box (FB): close → write back. Signatures can only be closed by a re-photo; a typed value is never accepted. Suitelet 2970 (GFS Dock Fulfillment & Receiving) is retiring, replaced by the lane (Michael, Oct 6).
Pass looks like
The value passes the rules, is written to the IR field with its source, and all holds are cleared.
Hold looks like
The reply fails validation → the gap task stays open.
Runs on
Planned: lane + Dock Portal 2542 (tablet twin) + Slack. Not 2970 (retiring, replaced by the lane).
Engine or model
Rules.
Where data lives
IR fields listed in R11.
Live or not / numbers
PLANNED. Zero NetSuite writes today.
Next step
Build the lane write path for the FS/lot fields (build list), then the 2542 tablet twin.
Linked boxes: these steps live in other sections of the same flow (no duplicates)
7Item Receipt with the food-safety (FS) fieldsFields exist · Save locked
What it checks
Sheet → IR: custbody3 seal #, custbody_gfs_fs_trailer_temp, custbody_gfs_fs_chk_temp (derived from storage class), fs_seal_applied, fs_reefer, fs_chk_sanitary + fs_chk_odor, fs_chk_pest, fs_chk_damage, fs_trailer_cond (derived), custbody_gfs_bol_number (PROPOSED; goes to memo until it exists), custbodycarrier, line quantity, custcol_gfs_lot_code + custcol_gfs_lot_expiry, fs_disposition + fs_status, fs_exceptions, fs_inspected_by, received-by (PROPOSED), fs_inspected_at, fs_pictures + file attach.
Pass looks like
IR saved (after unlock) with every sheet field + read-back confirmed.
Hold looks like
Any open hold → no Save. Save is locked today.
Runs on
NetSuite IR (Save LOCKED). This is box F1 (Item Receipt if received) in step 7, carrying the sheet fields. Read-back is still F3.
Engine or model
user-netsuite MCP (after unlock only).
Where data lives
NetSuite Item Receipt body + line fields.
Live or not / numbers
NetSuite audit, Oct 5 (read-only): all 24 custbody_gfs_fs_* fields and the lot/expiry columns are 0% filled on IR/IF, ever (57 IR / 180 IF at loc 4 in the last 60 days). Oct 6 ruling: Suitelet 2970 is retiring (replaced by the lane), so these fields will be written by the lane at Save, not by 2970. 2971 FS log stays. BOL # and received-by fields don't exist yet. Save LOCKED, zero NetSuite writes.
Next step
Confirm nothing else depends on 2970 before retiring it; create the BOL # field (build list).
9Bill / Invoice copy-forwardPLANNED · not built
What it checks
Bills are built from the PO and invoices from the SO, not from the IR/IF. So nothing set on an IR/IF flows forward natively. Planned User Event script: when a Bill or Invoice is created, pull BOL #, carrier, trailer and seal from the linked IR/IF (PO→IR / SO→IF via previoustransactionlink). This needs custbody_gfs_bol_number applied to VendBill + CustInvc too. Linked from H3 (AP matches the vendor bill to the receipt) and H4 (AR invoice).
Pass looks like
The Bill/Invoice carries the same BOL #, carrier, trailer and seal as the dock record.
Hold looks like
No linked IR/IF, or more than one → leave blank and flag it (never pick one).
Runs on
Planned: NetSuite User Event script on Vendor Bill + Invoice.
Engine or model
SuiteScript UE (not written).
Where data lives
Audit section 3: 0% of FS/lot values reach Bills/Invoices. Carrier is on IR 22.8% / IF 29.4%, only copied from the PO/SO.
Live or not / numbers
PLANNED. Not written, not deployed.
Next step
Claude Code build list. Deploy only after Michael OKs it.
LOOPLearning loopPLANNED · not built
What it checks
Closed gaps (the owner's answer), Label Studio gold tags, and a one-time template for each new vendor all feed back into the reader and the gap filler. Accuracy is scored on a frozen 20-page holdout that is never used for training. That holdout is the gate before any auto-post. It sits beside the existing training (TRAIN), score (SCORE) and feedback (FB) boxes, and closed gaps from step 3.10 arrive through FB.
Pass looks like
The holdout score clears the bar → counts toward unlock (GO).
Hold looks like
Holdout not frozen, or the score misses → stays in shadow.
Runs on
Studio: Label Studio + reader.
Engine or model
Label Studio gold + reader re-score.
Where data lives
Gold tags (Gate 1 vendors: Gregory Packaging, General Mills, Kellogg, Amerifilm).
Live or not / numbers
PLANNED. The 20-page holdout is NOT frozen yet.
Next step
Freeze the holdout. Add real RS-v2.1 dock photos to it.
GOGo-live pathPLANNED · not built
What it checks
Phase 1: the RS-v2.1 sheet prints live at the dock while the bot runs zero-Save shadow. It reads, cross-checks and drafts gap tasks, and writes nothing to NetSuite. Phase 2: Saves unlock only after clean weeks of shadow, a passing holdout score, the bol@ sender allowlist being live, and Michael's explicit word. Same gate as the existing 'Claude post gated by score' box (CL).
Pass looks like
All of: clean weeks + holdout pass + allowlist live + Michael unlock.
Hold looks like
Any of them missing → stays in shadow.
Runs on
Lane-wide.
Where data lives
Gate ladder: spine TESTING.md / PRD.md.
Live or not / numbers
PLANNED. Today: Zero NetSuite Saves, allowlist NOT LIVE, holdout NOT frozen.
Next step
Michael OKs the recvXml() port so the sheet can start printing (phase 1).
New boxes from the Oct 6 logic review (each is also in the diagram)
3.7 ENDRejected: load refused (end)PLANNED · not built
What it checks
End of the path when the dock ticks REJECT (H-DECISION), for example temp out of limit, seal cut, pests, or a crushed/wet load. The load is refused: no Item Receipt is created, and the refusal is logged in the Food Safety Log (2971) with the sheet photo and reason.
Time
same day as the truck
Pass looks like
Refusal logged with photo + reason; no IR is posted. Any notices are drafts for Michael to approve.
Hold looks like
REJECT is a human-only decision (dock / supervisor). The bot never picks REJECT and never flips it to ACCEPT.
Runs on
Dock + lane (planned).
Engine or model
Rule on the sheet decision box; drafts only.
Where data lives
Sheet photo on Studio; FS log entry in NetSuite 2971 (write locked until prove unlock).
Live or not / numbers
PLANNED · not built. ACCEPT/HOLD/REJECT → NetSuite disposition mapping, PCQI temp limits and who gets told are still to confirm.
Next step
Confirm the REJECT disposition and the notice list.
1 SUBMITDock Portal 2542 'Submit' tab (planned)PLANNED · not built
What it checks
Michael's idea (Oct 6): a 'Submit' tab in the Dock Portal 2542 left nav, on the PIN deployment (Heartland / Quaker). The dock picks the open PO or SO from a list, attaches phone photos (the RS-v2.1 receiving sheet + the vendor BOL) and a short text note. It posts into the SAME intake as bol@: one door, two entrances. The PO/SO is pre-linked, so there is no subject/ref parsing, and the portal PIN session counts as a trusted sender, so it satisfies the allowlist. bol@ email stays as the backup for vendors and anyone off the portal.
Time
seconds at the dock
Pass looks like
Photos land in a File Cabinet folder and a queue/forward record enters the lane intake with the PO/SO id, the PIN user, the note and the files; the ticket is then sorted (step 2) like any bol@ ticket.
Hold looks like
Upload fails or no open PO/SO is picked → nothing is queued and the screen says so; the dock can still email bol@. One PO/SO per submit (one BOL per ticket).
Runs on
NetSuite: Dock Portal 2542 (PIN deployment customdeploy_gfs_dock_portal), planned.
Engine or model
SuiteScript (file upload + queue/forward record). No Item Receipt / Fulfillment writes.
Where data lives
File Cabinet folder (to choose) + a queue record or a forward into the bol@ intake (to choose); Studio ticket afterwards.
Live or not / numbers
PLANNED · not built. Added to the Claude Code build list (Oct 6). Zero NetSuite writes today.
Next step
Pick the File Cabinet folder and the hand-off (queue record vs forward to bol@); build after Michael OKs it.
1Allowlisted sender? (planned)PLANNED · not built
What it checks
Check: is the sender on the bol@ allowlist (dock staff, known vendors and carriers, our own inboxes that forward in)? With one door (bol@), this is the single place to stop unknown senders before anything is read or matched.
Time
seconds, part of intake
Pass looks like
Known sender → attachments saved to Studio (A3) and the ticket is sorted (step 2).
Hold looks like
Unknown sender → Sent to Michael (N) with the reason 'unknown sender'. Nothing is read into NetSuite. Not a Save.
Runs on
bol@ intake on the Mac Studio (planned).
Engine or model
Rules: allowlist lookup (list not built).
Where data lives
Allowlist: not created yet.
Live or not / numbers
PLANNED · not built. Today 'unknown sender' only exists as a hold reason in the N / CL notes. A live allowlist is one of the go-live gates (GO) before the first Save.
Next step
Michael confirms who goes on the list.
6 ENDCompare-ok · close (no new Save)Done (read-only)
What it checks
End of the path when the receipt or fulfillment already exists and the paper matches it. Nothing new is created.
Time
target: a few minutes, part of the clean path
Pass looks like
Paper matches the existing IR/IF → the ticket closes as compare-ok in shadow.
Hold looks like
Any difference goes to Michael instead (E2 → N).
Runs on
BOL x Item Receipt Bot (Grok), shadow mode
Engine or model
Read-only compare.
Where data lives
NetSuite (read only) + the saved ticket on the Mac Studio.
Live or not / numbers
Live compare-only (the 'matches' outcome in E2's note, now drawn). Zero Saves until prove unlock.
7 OUTOutbound check + FS fields on IFPLANNED · not built
What it checks
The RS-v2.1 sheet is receiving-only. Shipped loads have no matching dock check yet: trailer temp / pre-cool, seal # applied, trailer clean / no pests, lots and expiry picked. These belong in the Item Fulfillment FS fields, which the Oct 5 audit found 0% filled on all 180 IF (loc 4, last 60 days).
Time
not timed yet
Pass looks like
Outbound checks captured and carried on the IF once Saves unlock.
Hold looks like
Missing outbound temp / seal / lot → gap, never invented (same rules as receiving).
Runs on
Lane + Dock Portal 2542 (planned). Not 2970 (retiring).
Engine or model
Not designed yet.
Where data lives
IF custbody_gfs_fs_* fields (they exist, 0% filled).
Live or not / numbers
PLANNED · not built, not designed. Shipped tickets today go B3 → step 4 with no dock sheet.
Next step
Decide: an outbound twin of the sheet printed from 2542, or a 2542 tablet screen.
Dock form v0.1 draft · superseded for receiving
- One form per order, attached to the BOL.
- Corner marks so phone photos can be straightened.
- One character per box, block capitals, so the scanners read it cleanly.
- Office pre-prints the order number, expected lines and a QR code.
- Direction, location (Heartland loc 4 / Quaker loc 18), seal, temp, lots, expiry and short / over / damaged per line.
Draft v0.1, Sep 24, 2026. Everything on it (order, vendor, items, QR) is sample data.
12. NetSuite fields + Claude Code build list (new Oct 5-6)
AUDNetSuite dock field audit (Oct 5, read-only)Done (read-only)
What it checks
Read-only SuiteQL + metadata audit of every dock field on IR/IF/PO/SO/Bill/Invoice. Window: loc 4, 2026-08-06 → 10-05 (57 IR, 180 IF). Findings: all 24 custbody_gfs_fs_* fields and the lot/expiry columns are 0% filled on IR/IF, ever. Carrier is 23-29% (IR 22.8%, IF 29.4%), and every value was copied from the PO/SO, none captured at the dock. There is no BOL # field (proposed custbody_gfs_bol_number on IR/IF + Bill/Invoice). Bills/Invoices are built from the PO/SO, so copy-forward needs a script. The box copies of 2970/2971 write the FS fields, yet the fields are 0%, which fits 2970 not being used at the dock. Oct 6 ruling: 2970 retires (replaced by the lane); the lane writes the FS/lot fields; 2971 FS log stays. Cleanup flags: 3 seal fields (custbody3 Seal #, fs_seal_applied, fs_lock); Carrier List 407 mixes drivers/people and modes; loc 17 Delta Pak is still ACTIVE in NetSuite; loc 4 is named 'Global Food Solutions (HQ)' in NetSuite, not Heartland.
Pass looks like
Done. Read-only, zero writes.
Hold looks like
Couldn't confirm the creation context (systemnote query returned 0 rows), so 2970 usage is unverified. (Oct 6: moot for the dock, since 2970 is retiring; what remains is confirming nothing else depends on 2970.)
Runs on
user-netsuite MCP (read-only) + Monday get_board_info (read-only).
Engine or model
SuiteQL aggregates.
Where data lives
/workspace/bol-shadow/lanes/NS-DOCK-FIELD-AUDIT-2026-10-05.md + lanes/audit-raw/.
Live or not / numbers
Done Oct 5 ~7:45 PM ET. Zero NetSuite writes. Cleanup items are proposals only; nothing changed.
Next step
Michael decides: create the BOL # field, apply bol_attached to IR, inactivate loc 17, clean list 407.
CCClaude Code build list (Oct 6 addendum)PLANNED · not built
What it checks
Build list for Claude Code (dock-ops prompt addendum): 1) create custbody_gfs_bol_number on IR/IF (+ Bill/Invoice), as SDF XML for review; 2) custbody_gfs_fs_product_temp? (open question: product temp has no field today); 3) received-by on IR (custbody_gfs_received_by, or extend custbody_ndc_gfs_received_by); 4) apply custbody_gfs_fs_bol_attached to IR; 5) copy-forward UE script IR/IF → Bill/Invoice; 6) port RS-v2.1 into recvXml() (lib_gfs_bol.js 802283); 7) FS/lot writes come from the lane at Save, not 2970; confirm nothing else depends on 2970 before retiring it (2971 FS log stays); 8) dup_bol guard. Deploy only after Michael OKs it.
Pass looks like
Each item reviewed by Michael before deploy.
Hold looks like
No deploy without Michael's OK.
Runs on
Claude Code (Multica / Studio), SDF project.
Engine or model
Claude Code.
Where data lives
Studio ~/Documents/GFS-bots-mailbox/lanes/CLAUDE-CODE-DOCK-OPS-PROMPT-2026-10-02.md (addendum section) + box /workspace/bol-lane-spine/CLAUDE-CODE-DOCK-OPS-ADDENDUM-2026-10-06.md.
Live or not / numbers
PLANNED. Nothing built or deployed. Zero NetSuite writes.
Next step
Michael reviews the list and OKs it item by item.
13. Oct 7, 2026 changes: Dock Portal v1.3 / v1.4 / v1.4.1, RS-v2.2, BOL fixes, security
13a. Dock Portal v1.3 · LIVE
13a.1Dock Portal v1.3: shared 60 s board cache + no embedded fontsLIVE Oct 7
What it does
Dock Portal v1.3 keeps one shared board cache for 60 seconds, so the crew's phones reuse the same board data instead of each one re-querying. Embedded fonts are gone, so pages are about half the size.
Pass looks like
Board loads from the cache within 60 s of the last load; page weight about half of v1.2.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026.
13b. v1.4 share sheet · LIVE
13b.1v1.4: tap BOL or Receiving → share sheetLIVE Oct 7
What it does
Tapping BOL or Receiving opens a sheet with three buttons, in English and Spanish: Open / Abrir, Print / Imprimir and Text it / Enviar por texto.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026.
13b.2Texted links: one document, signed, stop at midnight NYLIVE Oct 7
What it does
A texted link opens one document only. It is signed with the PIN hash and stops working at midnight New York time.
Hold / fail looks like
Wrong signature, other document or past midnight NY → link refused (see STALE: tells the crew to refresh).
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026.
13b.3Kill switch SHARE_LINKS=0LIVE Oct 7
What it does
Setting SHARE_LINKS=0 turns the texted-link feature off at once.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7 (switch available; links currently ON).
13c. v1.4.1 fixes · LIVE
13c.1v1.4.1 fix: Past Due pallet math uses cases-per-palletLIVE Oct 7
What it does
Past Due pallet counts now divide by each line's own cases-per-pallet, and freight lines are left out of the math.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026 (v1.4.1).
13c.2v1.4.1 fix: Double tap on Text it starts only one shareLIVE Oct 7
What it does
A double tap on Text it no longer starts two shares; only one goes out.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026 (v1.4.1).
13c.3v1.4.1 fix: Stale links tell the crew to refreshLIVE Oct 7
What it does
An old or expired link shows a message telling the crew to refresh the portal and send a new one.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026 (v1.4.1).
13c.4v1.4.1 fix: Generic error page on the public sideLIVE Oct 7
What it does
Error pages show a generic message on the public side, so no internal details leak to whoever opens a link.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026 (v1.4.1).
13c.5v1.4.1 fix: Blocked pop-ups show an Allow pop-ups noticeLIVE Oct 7
What it does
When the phone or browser blocks the pop-up (Open / Print), the page shows an 'Allow pop-ups' notice instead of failing silently.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
LIVE Oct 7, 2026 (v1.4.1).
13d. Receiving sheet RS-v2.2 · LIVE (reader template pending)
13d.1RS-v2.2: full item name + description on taller rowsLIVE Oct 7
What it does
The receiving sheet now prints the full item name and description on taller rows (no truncation).
Runs on
NetSuite: recvXml() receiving sheet template
Live or pending
LIVE Oct 7, 2026.
13d.2RS-v2.2: QR tag RS-V2.2LIVE Oct 7
What it does
The sheet QR codes now carry the tag RS-V2.2.
Runs on
NetSuite: recvXml() receiving sheet template
Live or pending
LIVE Oct 7, 2026.
13d.3Reader routes by tag (rs-v2.2 template pending real reprint)Partly live
What it does
The photo reader reads the QR tag and routes to the matching template (RS-V2.1 or RS-V2.2). Routing is live; the rs-v2.2 reader template is still pending a real printed sheet (PO12993 reprint, box PD1).
Runs on
Studio reader
Live or pending
Routing LIVE Oct 7; rs-v2.2 template PENDING a real reprint.
Next step
Reprint PO12993 on RS-v2.2, photo it, build the template from it.
13d.4Units fix: blank units print as units; non-case units totalled separatelyLIVE Oct 7
What it does
Lines with no unit print 'units' instead of a blank, and non-case units (each, lb, etc.) are totalled on their own instead of being added into cases.
Runs on
NetSuite: recvXml() receiving sheet template
Live or pending
LIVE Oct 7 on the sheet. The Dock Portal units patch is NOT deployed yet (box PD4).
13e. BOL print fixes · LIVE
13e.1BOL fix: One page for long ordersLIVE Oct 7
What it does
BOL print fix (Oct 7): one page for long orders.
Runs on
NetSuite: BOL print template
Live or pending
LIVE Oct 7, 2026. A real SO14501 BOL print is still pending (box PD2).
13e.2BOL fix: Clean totals cellLIVE Oct 7
What it does
BOL print fix (Oct 7): clean totals cell.
Runs on
NetSuite: BOL print template
Live or pending
LIVE Oct 7, 2026. A real SO14501 BOL print is still pending (box PD2).
13e.3BOL fix: A dash instead of a false 0LIVE Oct 7
What it does
BOL print fix (Oct 7): a dash instead of a false 0.
Runs on
NetSuite: BOL print template
Live or pending
LIVE Oct 7, 2026. A real SO14501 BOL print is still pending (box PD2).
13e.4BOL fix: MIXED LOAD temperature textLIVE Oct 7
What it does
BOL print fix (Oct 7): mixed load temperature text.
Runs on
NetSuite: BOL print template
Live or pending
LIVE Oct 7, 2026. A real SO14501 BOL print is still pending (box PD2).
13f. Security role 1266 · created, NOT switched on
13f.1GFS Dock Portal Runner role (id 1266) createdLIVE Oct 7
What it does
The GFS Dock Portal Runner role (internal id 1266) is created. It is NOT yet the run-as role on deployment 10593; the steps below must finish first.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
Role created Oct 7; NOT switched on.
13f.2Security: Subsidiaries = AllPENDING
What it does
Set the role's Subsidiaries to All.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
PENDING (Oct 7).
13f.3Security: Other Addresses → ViewPENDING
What it does
Set the Other Addresses permission to View.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
PENDING (Oct 7).
13f.4Security: BaselinePENDING
What it does
Record a baseline of the portal on the current role before switching.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
PENDING (Oct 7).
13f.5Security: Switch deployment 10593 to role 1266PENDING
What it does
Switch deployment 10593 to run as role 1266.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
PENDING (Oct 7).
13f.6Security: Phone tests T1–T8PENDING
What it does
Run phone tests T1 to T8 on the switched deployment; roll back if any fail.
Runs on
NetSuite: roles / script deployment 10593
Live or pending
PENDING (Oct 7).
13g. Pending items · dashed = PENDING
13g.1Pending: PO12993 reprint for reader templatePENDING
What it does
Reprint PO12993 on RS-v2.2 so the reader gets a real rs-v2.2 template.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.2Pending: Real SO14501 BOL printPENDING
What it does
Print a real SO14501 BOL to confirm the BOL fixes on paper.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.3Pending: TEST truck submitPENDING
What it does
Submit the TEST truck end to end.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.4Pending: Dock Portal units patch (not deployed)PENDING
What it does
The units patch for the Dock Portal board is written but NOT deployed.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.5Pending: Data: 12 SOs with blank ship-from (incl. SO13952)PENDING
What it does
12 sales orders have a blank ship-from, including SO13952.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.6Pending: Data: 71 lines with no unitPENDING
What it does
71 order lines have no unit.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.7Pending: Data: TT10401 classed FrozenPENDING
What it does
Transfer TT10401 is classed Frozen; check and fix.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
13g.8Pending: Data: garbled ½ textPENDING
What it does
Item text shows garbled '½' (encoding of ½); clean it up.
Runs on
NetSuite: Dock Portal 2542 / print templates
Live or pending
PENDING (Oct 7).
Where it stands
- Steps 1, 2, 5 and 6 run live in shadow mode — they do the work and report, but save nothing to NetSuite.
- Step 4 (reading the paper) is the weak link. The last four Monday OCR batches locked only about 26% of pages (42 of 160).
- Steps 7 and 8 are built but locked. Nothing is saved until Michael unlocks the first watched Save.
- Step 9 emails fire only after a verified Save, and they stay as drafts Michael approves until he says they can send on their own.
- Still to confirm: the food safety@ address and Amanda's AP / AR addresses.
- Pending / not built (shipped lane only, after verified Item Fulfillment): auto invoice the customer (AR) — today Amanda gets a draft email only; if there is a backorder, auto-close it; post a note to a Slack channel (channel not chosen). Time not timed yet. Zero NetSuite Saves until prove unlock. These do not run today.
- Pending / not built (collections, after invoice · future): collections with terms applied; dunning brackets at 3, 5, 7, 10, 14, 21, 30, 45, 60, 75, 90, and 120 days past due; plus dunning letters and the automated collections process. Letter copy, fees, and Slack channel not invented here. Time not timed yet. Zero NetSuite Saves. Not live.
- Step 3 (new Oct 5): the receiving sheet RS-v2.1 loop is PLANNED. The sheet design + reader are done and tested on synthetic photos only (QR 12/12, ticks 61/61, 94% auto-accepted, 0 wrong). The recvXml() port, Studio reader install, cross-check, gap filler, Slack gap task and write-back are not built. The same goes for the linked boxes: FS fields on the IR (step 7), the copy-forward script (step 9) and the learning loop. Go-live: the sheet prints first while the bot stays in zero-Save shadow; Saves unlock only after clean weeks + the holdout + the bol@ allowlist.
- Step 12 (new Oct 5): NetSuite dock field audit (read-only). The food-safety fields and lot/expiry columns hold no data (0% on IR/IF, ever). No BOL # field exists. Cleanup proposals: 3 seal fields, carrier list mixes drivers, loc 17 Delta Pak still active, loc 4 is named "Global Food Solutions (HQ)". No changes made.
- Step 13 LIVE (Oct 7): Dock Portal v1.3 (60 s shared board cache, no embedded fonts, ~half-size pages); v1.4 share sheet (Open / Abrir, Print / Imprimir, Text it / Enviar por texto; links open one document, signed with the PIN hash, stop at midnight NY; kill switch SHARE_LINKS=0); v1.4.1 fixes (pallet math by cases-per-pallet without freight, one share per double tap, stale-link refresh notice, generic public error page, Allow pop-ups notice); RS-v2.2 sheet (full names, QR tag RS-V2.2, units fix); BOL fixes (one page, clean totals, dash not false 0, MIXED LOAD temp text).
- Step 13 PENDING (Oct 7): role 1266 is created but NOT switched on deployment 10593 (still to do: Subsidiaries=All, Other Addresses to View, baseline, switch, phone tests T1–T8); rs-v2.2 reader template needs the PO12993 reprint; real SO14501 BOL print; TEST truck submit; Dock Portal units patch (not deployed); data cleanup (12 SOs with blank ship-from incl. SO13952, 71 lines with no unit, TT10401 classed Frozen, garbled '½' text).
