# Shipment, Order, Pack, Item: where the HL mapping stops being derivation

> transactions.dev · by visibility.cloud — 2026-07-31
> Written for the supply-chain ISV / traceability-vendor engineering org — the integration engineer who reads HL loops with a pencil and wants the tree, not the folklore.

One thesis: **almost every step from HL loop to AggregationEvent is a
derivation an engineer can check against the file, and exactly one step
is a ruling — tare-versus-pack SSCC nesting — so what a governed pipeline
owes you is not cleverness but bookkeeping: the ruling named, pinned, and
changed only by journal.** That the 856's parent-pointer hierarchy is
structurally an EPCIS aggregation tree is this site's standing premise,
argued as the projection claim in
[the aggregation-tree post](/blog/the-856-is-already-an-aggregation-tree/);
this page takes it as read and walks the mechanics to the one place
derivation runs out.

## HL mechanics, in original prose

The HL segment is a linked list pretending to be a hierarchy. Three
elements do the work: HL01 is the node's own sequence number, HL02 points
at the parent's HL01 (empty for the root), and HL03 declares what kind of
node this is — shipment, order, tare, pack, item. The nesting is not
expressed by position or indentation; it is expressed by those parent
pointers, which means the hierarchy is a runtime structure you build,
not a grammar you match. (Element-level X12 detail belongs to the
licensed reference — the segment authority for the 856 is
[Stedi's 856 page](https://www.stedi.com/edi/x12-008010/856), linked here
and never republished.)

A synthetic, license-clean fragment from the golden join corpus —
`856-ship-notice-01.edi`, annotated:

```text
HL*1**S~                          node 1, no parent — Shipment
HL*2*1*O~                         node 2, parent 1  — Order
PRF*PO-8812~                        the order's PO reference
HL*3*2*P~                         node 3, parent 2  — Pack
MAN*GM*006141411234567890~          the pack's SSCC
HL*4*3*I~                         node 4, parent 3  — Item
LIN**UP*614141123452*LT*L42~        GTIN (UPC form) + lot
SN1**12*EA~                         quantity 12 each
HL*5*3*I~                         node 5, parent 3  — Item
LIN**UP*614141765430~               GTIN, serialized below
SN1**2*EA~
REF*SE*1001~
REF*SE*1002~
```

Read the pointers and the tree is: a shipment containing an order line
context, containing one pack (an SSCC-labeled unit), containing two item
nodes. A deeper document adds a T (tare) level between order and pack —
a pallet of cases — and nothing about the reading changes: the pointers
still define the tree, and every SSCC-bearing node is still one
aggregation.

## The isomorphism, level by level

EPCIS aggregation speaks parent and children: an AggregationEvent asserts
that a parent identifier (typically an SSCC) contains child EPCs or child
quantity classes. The 856's levels map onto that grain directly:

- **S (shipment)** — the top handling context; compiles to the shipping
  ObjectEvent that carries the document's references.
- **O (order)** — not a physical node at all; contributes its PRF as a
  `po` entry on `bizTransactionList` and emits no event.
- **T (tare) / P (pack)** — physical packaging nodes; each SSCC-bearing
  node compiles to one AggregationEvent with that SSCC as `parentID`.
- **I (item)** — the leaves; they emit no events and resolve to
  `childEPCs` (serialized) or `childQuantityList` (lot/class grain) on
  the nearest ancestor's event.

And the compiled output for the fragment above, from the corpus fixture's
expected EPCIS document:

```json
{
  "type": "AggregationEvent",
  "action": "ADD",
  "bizStep": "packing",
  "disposition": "in_progress",
  "parentID": "https://id.gs1.org/00/006141411234567890",
  "childEPCs": [
    "https://id.gs1.org/01/00614141765430/21/1001",
    "https://id.gs1.org/01/00614141765430/21/1002"
  ],
  "childQuantityList": [
    { "epcClass": "https://id.gs1.org/01/00614141123452/10/L42",
      "quantity": 12 }
  ]
}
```

Nothing in that event was invented. The parent came from MAN, the serial
children from LIN plus its serial references, the class child from LIN's
lot qualifier plus SN1's count. The costume changed; the tree did not.

## Where the identifiers live

Three segments carry the GS1 identity the tree hangs on — the identifier
grammar itself is [barcoding.dev](https://barcoding.dev)'s layer:

- **MAN**, with the `GM` qualifier, carries the SSCC-18 that becomes
  `parentID` (as a canonical GS1 Digital Link URI, AI `00`).
- **LIN** qualifiers `UP` / `EN` / `UK` carry UPC-12, EAN-13, or GTIN-14;
  all normalize to GTIN-14 before entering an EPC URI —
  [the join-key post](/blog/the-gtin-is-the-join-key/) covers the
  indicator-digit and check-digit traps.
- **SN1** carries the quantity that becomes the `childQuantityList`
  entry's count at lot grain.

## The one place the mapping is a ruling

Almost everything above is derivation: the tree is in the file, the grain
is in the standard. But one decision is not derivable: when both a tare
node and its pack children carry SSCCs, does the compile emit one event
per packaging node (nested aggregation) or flatten to the top unit? The
pipeline's answer — one AggregationEvent per SSCC-bearing node, nesting
preserved — is recorded in the pinned join map with a provenance block,
and it changes only by journaled human ruling, never silently. A
malformed tree (an orphan HL02, a cycle, a level-order violation) compiles
to nothing: a typed refusal, because a custody tree with a broken pointer
is not evidence, it is a bug wearing evidence's clothes.

That honesty is the difference between a mapping and a heuristic — and it
is this page's whole point: every other line above is checkable
derivation; this line is governance, and it is labeled as such. The
projection argument itself stays with
[the aggregation-tree post](/blog/the-856-is-already-an-aggregation-tree/),
the canonical statement of the premise. The full run, envelope to
validated EPCIS document with the verdicts printed, is
[the compile post](/blog/from-856-to-epcis-document/), and what the
events make possible at the dock is
[the reconcile post](/blog/the-asn-said-48-the-dock-saw-47/).

When you have an 856 of your own to hold against this mapping,
[bring it to the door](/get-access/) — the onboarding flow asks which
sets are on the wire today and who owns the trading-partner map.

---
Access: https://transactions.dev/get-access/ · All posts: https://transactions.dev/blog/ · Machine face: https://transactions.dev/llms.txt
