78cdae7249c8609dcbc743e996ea7e5eec33d75a
max
  Mon Aug 17 08:15:39 2026 -0700
lrSv: fix off-by-one anchor base in deletion coordinates across converters, refs #38099

VCF/pangenome deletions carry a non-deleted anchor (padding) base at POS.
Several lrSv converters set chromStart = pos-1, which includes that anchor, so
each deletion was 1 bp too wide on the left and svLen was 1 too big. Callsets
handled this inconsistently, so the same deletion appeared at offset coordinates
and failed to merge in lrSvAll.

For deletions only (INS/INV/CPX unchanged), advance chromStart past the anchor
so the interval covers exactly the deleted bases (svLen == |SVLEN|). Verified
against the hg38 reference: the old left base is present in both REF and ALT
(i.e. retained by the sample), so it should not be inside the deletion.

Fixed 11 converters: lrSv1kLin1218VcfToBed, lrSv1kgOntVcfToBed,
lrSvGustafsonVcfToBed, lrSvGa4kSvVcfToBed, lrSvDecodeVcfToBed,
lrSvAou1kCsvToBed, lrSvColorsDbSvVcfToBed, lrSvCardBbToBed, lrSvAprVcfToBed,
lrSvCpc1VcfToBed, lrSvVcfToBed (generic, used by han945).

Left unchanged, verified already anchor-correct: hgsvc3 and hgsvc2 (0-based
source), hprc2v21 (Ro converter prefix-trims), noyvert/tommoJp (POS is the
first deleted base), chirmade101 (1-based-closed source).

Rebuilt all affected bigBeds (hg38 + hs1 where present) and the lrSvAll merge:
3,111,026 -> 2,963,093 rows as ~148k duplicate deletions now merge.

diff --git src/hg/makeDb/scripts/lrSv/lrSvCpc1VcfToBed.py src/hg/makeDb/scripts/lrSv/lrSvCpc1VcfToBed.py
index 252c2a8ef17..35738f006f9 100755
--- src/hg/makeDb/scripts/lrSv/lrSvCpc1VcfToBed.py
+++ src/hg/makeDb/scripts/lrSv/lrSvCpc1VcfToBed.py
@@ -80,30 +80,35 @@
             if a == "1":
                 ac += 1
         if has_called:
             ns += 1
     return ac, an, ns
 
 
 def emit(site, fout):
     classes = site["types"]
     sv_type = next(iter(classes)) if len(classes) == 1 else "MIXED"
     sv_type = normalizeSvType(sv_type)
     rgb = svColor(sv_type)
     chrom = site["chrom"]
     start = site["pos0"]
     end = start + max(site["ref_len"], 1)
+    # For DEL, POS is the non-deleted anchor base; drop it from the left (end
+    # already = pos-1+ref_len). The shared prefix is >=1 base so this never
+    # over-shifts. Only DEL moves; INS/MIXED keep the anchor.
+    if sv_type == "DEL":
+        start += 1
     af = (site["ac_sum"] / site["an"]) if site["an"] else 0.0
     score = min(1000, max(0, int(round(af * 1000))))
     svLen = end - start
     insLen = site["max_ins"] if sv_type == "INS" else 0
     featLen = insLen if sv_type in ("INS", "MEI") else svLen
     name = svName(sv_type, featLen, site["ac_sum"])
     row = [
         chrom, str(start), str(end),
         name,
         str(score),
         ".",
         str(start), str(end),
         rgb,
         sv_type,
         str(svLen),