Merge and aliases

One physical product, one sku_id. 23 open candidates across 2,159 SKUs · 214 live aliases · scanner last ran 29-07 03:15 Dubai

A merge is irreversible and it never rewrites a ledger row. The losing SKU is frozen, its code becomes an alias, and any live balance moves by a compensating pair (-q on the loser, +q on the winner) at every location and stock type where it is non-zero. History keeps pointing at the original sku_id, so the hash chain still verifies and the month-end close is still reproducible. See the chain
SKUs 15
Style groups 5
Aliases only 3
Blocked 4
History 41
All 15
Barcode match 6
Prefix variant 4
Retired code 3
Cross brand 1
Composite pair 1
Blocked 4
Dismissed 9
Weights version v1 · a historical score always explains itself with the weights that produced it

Suspected duplicate SKUs

Ranked by composite similarity. Selecting a row loads it into the canvas below.

6 at 90+5 at 80 to 894 below 80
Pair Similarity Matched signals Units A Units B Ledger rows State Action
*
MSR31-07
Sereen Rose / Tan · CIC
vs
MSR-31-7
Sereen Rose Tan · inv1 excel import
91
barcode supplier part cost 17541,204 in canvas
LBA01
Leather bracelet black · 12oz
vs
LBA-01
Leather bracelet blk · shopify legacy
96
barcode image hash title 318112,880 open
ALM02
Almas pendant · active
vs
ALM02-OLD
Almas pendant · retired 03-2025
99
variant id barcode cost 960744 alias only, zero balance
FIS12-B
Fisha set bracelet · component
vs
FIS12B
Fisha bracelet · pc map import
97
barcode recipe leaf cost 442508 member of 4 recipes
GIFTBOX_MSR31_JD
composite · 2 components
vs
GB_MSR31_JD
composite · 2 components · same BOM
94
identical BOM price title 00312 both composite, balance 0
STRAP-FST01
Strap tan 20mm
vs
STRAP_FST01
Strap tan 20mm · underscore variant
95
barcode supplier part recipe leaf 21071,096 open
CDW01
Classic dial watch
vs
CDW1
Classic dial watch · ops2 typo
93
title cost category 14211,822 open
JMS01
Joory set · jewellery_set parent
vs
JMS-01
Joory set · csd string
92
identical BOM title price 00402 open
MHS17
Hamdan watch · product_type Watch
vs
MHS-17
Hamdan · grn string
90
barcode supplier part 613690 open
AQB01
Aqua bracelet · Minimalist
vs
AQB01
Aqua bangle · Lulwa
88
code exact different brand 54391,010 do not merge · brand collision
MSR31-11
Sereen Gold / Mesh
vs
MSR31-11X
Sereen Gold Mesh · duplicate variant
86
title cost image hash 01896 blocked · open transfer TR-0938
PP02
Perfume Prive 50ml
vs
PP02-S
Perfume Prive tester · SAMPLE_POOL
74
title supplier part 560223,110 distinct product · sample SKU
DQR13
Dana ring
vs
DQR13P
Dana ring pearl
72
title category 88641,244 blocked · 3 units GIT_INBOUND
JRY12
Joory pendant
vs
JRY12P
Joory pendant pearl
68
title supplier 120771,918 distinct colourway
TNS01
Tanya ring
vs
TNS01-70
Tanya ring · size 70
61
title 3214560 size variant, not a duplicate
9 pairs dismissed this quarter stay dismissed until their signals change Scanner runs nightly 03:15 Dubai · n8n · alerts only on a new 90+ pair

Merge pair · MSR31-07 and MSR-31-7

Similarity 91 · resolve each field, pick the survivor, dry-run, then confirm by typing the phrase

dry-run required
Primary
MSR31-07 MSR-31-7
Survives post merge · the other is frozen and becomes an alias
Field
Entity AMSR31-07
Entity BMSR-31-7
SKU code
MSR31-07
MSR-31-7
Title
Sereen · Rose / Tan strap
Sereen Rose Tan
Shopify variant
40218835291
none · never linked
Barcode
6291033987421
6291033987421
Supplier part
CIC-SR31-RT
CIC-SR31-RT
Category
Women's Watches
Watches
Retail price
AED 749
AED 699 · stale
WAC
AED 206.13 · 5 layers
AED 0.00 · no layer
Lifecycle
active
active · will become merged_into

Aliases preserved after the merge · 5

MSR31-07-OLDx CIC-SR31-RTx MSR-31-7x MSR 31 7x MSR31_7x

Every retired string stays searchable. Historical POs, ledger rows, RFID encodes and webhook payloads keep their original reference, and lookups resolve forward through the alias chain.

Downstream impact

Ledger rows
1,204
stay on sku_id 2604
Serials
146
already keyed on sku_id
PO lines
9
2 open, repointed
Open cases
4
CSD 1 · drift 1 · RTS 1 · repair 1
Recipes
4
3 active, 1 inferred
Value at stake
36K
AED · above the 100K dual gate? no
POST /api/merge/dry-run winner=2604 (MSR31-07) loser=3311 (MSR-31-7) dry_run_hash = 8f3c41d0a97b taken 29-07 14:44 Dubai valid for 15 min ok balance check loser has 4 units at 1 location x 1 stock type ok in-flight check 0 IN_TRANSIT_* 0 GIT_INBOUND 0 IN_PRODUCTION 0 open transfer lines ok cost layers loser has none, nothing to merge into the FIFO stack ok alias collision 3 new alias strings, none already live elsewhere ok recipe membership loser is a leaf in 0 active recipes !! open cases 4 cases reference the loser string, they will be repointed to 2604 compensating rows to be posted (2): ledger sku=3311 loc=HQ_325 type=STOCK qty=-4 idem=merge:MRG-2026-0144:HQ_325:STOCK:out ledger sku=2604 loc=HQ_325 type=STOCK qty=+4 idem=merge:MRG-2026-0144:HQ_325:STOCK:in post state: balance(3311, *, *) = 0 balance(2604) = 175 + 4 = 179 zero rows updated · zero rows deleted · hash chain unaffected
Dual approval not required · AED 36K below the 100K threshold

Confirm irreversible merge

Type MERGE MSR31-07 MSR-31-7 exactly. The commit is rejected if the dry-run hash 8f3c41d0a97b no longer matches the world, which happens if anything moved in the last 15 minutes. Undo is not a button: it is a new merge in the opposite direction, logged as its own transaction.

Phrase incomplete · 20 of 26 characters

Alias registry

214 live aliases. Append only: an alias is never deleted, only superseded by a later merge.

AliasCanonicalKindSourceOriginLive from
MSR31-07-OLDMSR31-07renameshopifyvariant sku change14-03-2025
CIC-SR31-RTMSR31-07supplier partgrnPO-2025-0598 manifest02-11-2025
LBA-01LBA01importinv1excel_sale backfill31-12-2024
ALM02-OLDALM02mergemanualMRG-2026-013911-07-2026
JMS-01JMS01typocsdcase line item string18-05-2026
GB_MSR31_JDGIFTBOX_MSR31_JDmergemanualMRG-2026-014119-07-2026
STRAP_FST01STRAP-FST01importsku_intelparent_child_map04-02-2026
CDW1CDW01typoops2dispatch scan string27-06-2026
MSK01-VCRVCR01legacy_excelinv1strapchange transform31-12-2024
AQB01AQB01 · minimalistambiguousmanualbrand collision with Lulwa21-07-2026
FIS12BFIS12-Bimportsku_intelparent_child_map04-02-2026
MHN17unresolvedphantomcsdrejected expansion of MHS1717-07-2026
Depth after compaction: 1 · when B merges into A and later A into Z, every alias of B is re-inserted pointing at Z 1 phantom string quarantined, never given a canonical target

Merge history

Last 12 across all entity types. A reversal is a new forward transaction, not an edit.

MergeWhenLoser to winnerRowsUnits movedActorStateReverse
MRG-2026-014328-07 16:02DQR13-OLD to DQR131,2446AYcommitted
MRG-2026-014226-07 11:40TNS1 to TNS015602AYcommitted
MRG-2026-014119-07 09:18GB_MSR31_JD to GIFTBOX_MSR31_JD3120AYcommitted
MRG-2026-014015-07 14:55PP02-50 to PP023,11018SAcommitted
MRG-2026-013911-07 10:21ALM02-OLD to ALM027440AYcommitted
MRG-2026-013808-07 17:33LMW01-18 to LMW012084FBreversedby 0138R
MRG-2026-0138R09-07 08:02LMW01 to LMW01-182084AYreversal
MRG-2026-013702-07 12:09JFS01 to JFS01P1,1029AYcommitted
MRG-2026-013627-06 15:44CDW-1 to CDW011,8221SAcommitted
MRG-2026-013521-06 09:55FIS12E to FIS12-E4883AYcommitted
MRG-2026-013414-06 13:12ANQ04 to ANQ04P64011FBcommitted
MRG-2026-013302-06 10:47SHOPIFY-VAR-88214 to MSR31-039622AYcommitted
41 merges since phase 0 · 1 reversed · 0 ledger rows updated · 0 deleted The last row cleaned up an i2 placeholder SKU. There were 61 of them at import.

Safety rules

Irreversible
Undo is a separate forward merge in the opposite direction, with its own confirmation. There is no click-to-undo.
Never rewrites the ledger
Historical rows keep the original sku_id. Balance moves by a compensating pair. This is why the hash chain still verifies afterwards.
Blocked while stock is in flight
Any unit in IN_TRANSIT_CUSTOMER, IN_TRANSIT_RETURN, GIT_INBOUND or IN_PRODUCTION, or any open transfer line or GRN session, blocks the merge. Resolve the physical flow first.
Dual approval above AED 100K
Measured on the on-hand value at stake across both SKUs, not on supplier spend. Routes to the approvals queue.
Dry-run staleness
A commit carries the hash of its preview. If any counted object changed in the last 15 minutes the commit is rejected and a fresh preview is required.
Brand collisions are not duplicates
Minimalist and Lulwa reuse short codes. A cross-brand pair is marked ambiguous and resolved by brand at lookup time, never merged.

Detection signals · v1

Barcode / GTIN exact
0.30
Variant title trigram
0.20
Supplier part number
0.15
Cost within 2%
0.10
Image perceptual hash
0.10
Category + product_type
0.08
Co-location pattern
0.07

Fuzzy suffix guessing (append P / G / S, strip a hyphen) is allowed here only, as a scoring hint a human confirms. It is banned on every write path, because that is exactly how the SKU Intel splitter silently attributes value to a different product.

Candidate distribution

23 open pairs by similarity band

95 to 100 · 4 pairs · auto-flagged to Slack 90 to 94 · 2 pairs 85 to 89 · 3 pairs 80 to 84 · 4 pairs 70 to 79 · 5 pairs 60 to 69 · 3 pairs, mostly size and colour variants 95+9085 807060
merge candidate review likely a real variant

Alias registry growth

cumulative live aliases · 12 months

Aliases grew from 44 to 214 as INV1 and i2 history was imported Mar 2026 · INV1 excel_sale backfill added 87 import aliases in one week
Aug 25Nov 25Feb 26Jul 26
44 to 214 · the jump in March is the INV1 backfill, not a data quality problem

This month

Merges
6
5 committed, 1 reversed
Aliases added
19
14 from merges
Units moved
44
by compensating pairs
Chain breaks
0
verified nightly 02:00

The scanner runs nightly at 03:15 Dubai from n8n and posts to #n8n-updates only when a new pair scores 90 or above, deduped by pair id. A silent night is the normal night.

Connected to SKU detail Cost + price trends SKUs (Live) Classic table Audit log Drift Suppliers Locations Rules Settings RFID gates Shopify events