Meridian

Architecture · Mapping

How a product becomes a code, and a code becomes a match

Two mappings carry the whole product. A spreadsheet row becomes a brand family and an HS6 code; a customs declaration becomes an HS6 code; the two meet on that six-digit string. Every claim of the form this account buys what you sell is that string matching — nothing more. Both mappings lose information on the way, and this page is where that loss is stated.

Collapse at six digits10,6122,535national codes to HS6 · mean 4.2, median 2, worst 92
Products sharing a code81.6%sit at a code with ≥10 siblings · median 3, max 172
Codes that can never match27711,270 rows yielding fewer than six digits
Codes with a tariff description0of 10,889 — all source_only

Two mappings, one join key

What has to be true for 'this account buys what you sell' to mean anything?

Fig. 01

The brand family labels a row but never joins on one — it plays no part in matching. Everything the product claims rests on six digits derived twice, from two unrelated sources, by one rule.

Product → brand family

An exact string match. The Taxonomy sheet lists each family's products in a newline-separated cell; every Product Name* in the Client sheet is looked up in that list. All 2,512 match, none are duplicated, none are left over.

That is a fact about this file, not a property of the method

A perfect exact-match rate means Lanxess supplied the two sheets as a matched pair. There is no fuzzy fallback and no error path: change one character in either sheet and that product keeps its HS6, silently gets no family, and disappears from every family page while still counting toward code-level totals. Nothing would report it. If a future catalog arrives from a different process, check the unmatched count before trusting a family number.

2 pairs of families differ only by casing or trademark symbol — KALAMA® / Kalama®, KLARIX™ / Klarix®. They are kept separate rather than merged, because Lanxess listed them separately and collapsing a customer's own taxonomy is not our call — each page flags its twin. 27 of 234 families vanish entirely: every product they contain lacks a usable HS code, so nothing is left to match on.

Product → HS6

The HS Code (MARC) column is not uniform: 8 to 12 characters, some with a trailing letter (39092029000T), some dotted (3808.94.20.29). 128 of 2,512 products have no code at all and are excluded everywhere — they cannot be checked against any account.

Declaration → HS6

The customs field carries the code and its description: "90200090 - OTHER BREATHING APPLIANCES…". Both sides now use one rule (pipeline/hs_codes.py): take the leading code token, strip separators inside it, keep the first six digits. It cannot be “strip every non-digit” — that would absorb every digit in the description.

The two halves used to disagree

The pipeline took the leading digit run; the Lanxess catalog stripped all non-digits. On 3808.94.20.29 that is 3808 against 380894 — two values that never match, with no error raised. They agreed on all 10,889 observed customs codes only because none contained an internal separator; the spreadsheet did. Unified, verified as producing identical output on every code on both sides.

Source code lengthDistinct codesRowsReading
2 digits391,660Chapter or heading only — truncates to an hs6 that matches nothing
4 digits2389,610Chapter or heading only — truncates to an hs6 that matches nothing
6 digits1,03645,462HS6 exactly, internationally comparable
8 digits3,082239,606National extension; digits beyond six are country-specific
10 digits4,519299,715National extension; digits beyond six are country-specific
11 digits1,76049,179National extension; digits beyond six are country-specific
12 digits2152,689National extension; digits beyond six are country-specific

Digits seven onward are set by the importing country, so two countries' 8-digit codes are not comparable even when they describe the same goods. Six is the deepest level that means the same thing everywhere, which is why it is the join — not a convenience.

Rows the join cannot serve

277 codes across 11,270 rows yield fewer than six digits. They currently reach the payloads as two- and four-character “hs6” values that can never match a portfolio code. Dropping them is the right end state, but it moves figures app-wide, so it is a change to make deliberately rather than as a side effect — for now nonstandard_hs marks them.

6 codes (970 rows) claim chapter 00, which does not exist — zero-padded values like 0000320890, which almost certainly mean 320890. The padding is not stripped: “almost certainly” is a guess, and this pipeline flags rather than guesses. Chapters 98 and 99 are not flagged — both are legitimate national-use chapters and 36 codes (1,242 rows) use them here.

Some declaration lines name several codes at once — 320810 - PAINTS…, 320890 - PAINTS…, 320820 - PAINTS…. The first code takes the row's entire value, because the declaration states no split and inventing one would be a fabrication. multi_hs_code now marks these so the choice is countable rather than invisible.

What six digits cannot tell you

HS6National codes behind itRows
3824999213,354
854449588,044
3926905711,575
3208905438,579
27101950818

And from the other direction: the largest Lanxess fan-out is 391400 (172), 380899 (164), 390810 (156), 282110 (133), 390799 (110).

This is why opportunity is computed at HS6 and never at SKU

81.6% of Lanxess products share a code with ten or more siblings, and up to 92 national codes sit behind a single HS6 on the customs side. A match says the account imports something classified under this code. It cannot say which product, and no amount of presentation makes it able to. Every product page therefore labels its figure inherited from the code rather than attributed to the SKU.

What an HS description actually is

No official tariff nomenclature ships with this repository, so all 10,889 observed codes carry classification_source: observed_source_code and status: source_only. A description shown anywhere in the product is what the declaring party wrote on the paperwork — not what the tariff says the code means. Adding an approved official code table to the knowledge base promotes them to real matches; until then, treat descriptions as evidence of intent rather than classification.

Portfolio matching

A pasted portfolio goes through parseCodeList (lib/portfolio.ts), which truncates 8- and 10-digit national codes to six — the correct reading of a tariff line, not a guess — and reports anything unrecognised back instead of dropping it silently.

Of the 206 Lanxess codes, 178 appear somewhere in observed customs data. That is a larger number than the count of codes with demand shown on /lanxess, and both are correct: this one asks whether the code was ever seen at all, that one asks whether a covered account with usable coverage imported it. Neither is a market size.