BOL / Dock Receiving

GFS BOL Lane: How It Works

This page is the operating picture for GFS dock shipping and receiving (Heartland 4, Quaker 18). Ontology = the steps, checks, holds, and loops already on the page.

Locked target 2026-09-29: Suitelet side tab = own-facility door (Heartland 4 / Quaker 18); email = backup; drop ship keeps its own door; every door feeds bol@ lane. Suitelet NOT built — Mike recodes. Gold tags + holdout score accuracy; public scores today are engine-agree only. Shadow mode — zero NetSuite Saves until prove unlock. Updated Sep 29, 2026.

Time: TARGET — clean single-ref ticket (known vendor template, fields agree, PO/SO match, no existing receipt/fulfillment): a few minutes from drop to draft once the Suitelet exists. Today shadow does not Save. Held path (missing lot/expiry, unknown sender, slashed multi-ref, loc 17, engines disagree, qty off) waits on a person — not a few minutes. Hourly weekday intake check = email backup poll, not Suitelet speed.

Oct 6, 2026 update: the receiving sheet RS-v2.1 loop is now step 3, in real dock order. The sheet is printed and filled at the dock when the truck arrives, and its photo travels with the BOL into bol@ (step 1). The lane checks it right after sorting (step 2): QR check → 2-engine reader → sheet vs BOL cross-check → holds → gap filler → Slack gap task → write-back. Clean tickets go on to the paper read and match. Old steps 3–10 are now 4–11. The IR with FS fields sits in step 7, Bill/Invoice copy-forward in step 9, and the learning loop beside score/training. Step 12 = Oct 5 NetSuite field audit + Claude Code build list. Most new boxes are PLANNED (dashed red). All 37 original boxes are kept. Still zero NetSuite writes.

Oct 6 review fixes: every decision now shows all its branches, and every hold has a way out (Sent to Michael → resolved → back into the lane). New end boxes: Compare-ok · close and Rejected: load refused. New planned checks: Allowlisted sender? at intake and Outbound check + FS fields on IF for shipped loads. One door = bol@. The duplicate-BOL guard sits in step 6. Emails are marked DRAFT. The score gate unlocks the first Save (step 7). Michael ruled that Quaker 18 stays valid next to Heartland 4; loc 17 still holds. Click any box for its "Oct 6 review fix" note.

Oct 6 ruling (12:55 PM): Dock Portal 2542 stays the dock team's live screen (PIN, Heartland / Quaker) and prints the RS-v2.1 sheet via recvXml(). Suitelet 2970 (GFS Dock Fulfillment & Receiving) is retiring, replaced by the lane (sheet photo + bol@ reader + gap tasks). FS/lot writes come from the lane at Save. The 2971 Food Safety Log stays. Planned (Michael's idea): a Submit tab in 2542 where the dock picks the open PO/SO, attaches the sheet + BOL photos and a note; it feeds the same intake as bol@ (one door, two entrances). bol@ email stays as the backup.

Oct 7, 2026 update: new section 13 on the diagram and in the notes: Dock Portal v1.3 (60 s board cache, half-size pages), v1.4 share sheet (Open / Print / Text it), v1.4.1 fixes, receiving sheet RS-v2.2, BOL print fixes are LIVE (green). The security role 1266 switch, reprints, TEST truck, units patch and data cleanup are PENDING (dashed red). Nothing earlier was removed.

Color key: Green = works today Yellow = works but weak Red = blocked / not started Gray = built but locked Purple = email steps added Sep 24 (all DRAFT first) Blue = comes to Michael Dashed red = PLANNED (not built) Rounded box = ticket ends here Dashed arrow = link / feedback, not the main path
The flow, top to bottom 👆 Click any box for details fit
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 built

2Sort 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 weak

3Receiving 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 planned

4Read 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 link

5Match 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 pending

6Duplicate 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 today

7Save 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 locked

8Dock log

Adds the Dock Fulfillment and Receiving entry plus the Food Safety Log entry.

Built but locked

9Tell 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 locked

10After 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 built

11Collections (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 built

12NetSuite 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 planned

3. Receiving sheet RS-v2.1 loop (new Oct 5)

Where it sits: step 3, in real dock order. The sheet is printed and filled at the dock when the truck arrives (3.1–3.2). Its photo goes to bol@ with the vendor BOL (3.3) and enters through step 1 like every ticket. After step 2 sorts it as received, the lane checks the sheet (3.4–3.10) before the paper read (step 4) and the match (step 5). What changed Oct 5 (evening): the existing Receiving Sheet that the Dock Portal (script 2542) prints from 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.
Receiving sheet RS-v2.1 preview filled with SAMPLE data
SAMPLE DATA ONLY RS-v2.1 preview (Chromium render of the BFO XML, not the real NetSuite PDF).
Scan stress test contact sheet, RS-v2 vs RS-v2.1 on 12 synthetic photos
Scan stress test, Oct 5: 12 synthetic phone photos (flatbed, skew, shadow, glare, blur, low light, fold, downscale, warm paper, worst combo). RS-v2.1: QR 12/12, corners 12/12, ticks 61/61, 337/360 values auto-accepted (94%), 0 silently wrong, the rest held. Synthetic handwriting, so validate on 20+ real dock photos first.
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

Superseded for receiving (Oct 6): the RS-v2.1 receiving sheet (step 3) replaces this Sep 24 v0.1 draft for received loads. v0.1 stays here for history and as a starting point for the planned outbound (shipped) check (box OUT in step 7). Do not print v0.1 for receiving.
GFS Dock Receiving / Shipping Form v0.1 draft, filled with SAMPLE data
SAMPLE DATA ONLY
  • 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.
Download the PDF

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

Green = LIVE today (Oct 7). Dashed red = PENDING. Yellow = partly live. Every row is also a box in section 13 of the diagram; click either for the same notes.

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