# RERUN_NOTES -- NSCLC hotspot multiplex ddPCR panel

Engineering/biology notes for the next agent. Audience: a re-run, not the PDF reader.

## 1. What this project did
De novo **multiplex ddPCR panel** for 6 actionable NSCLC drivers from **cfDNA**, Bio-Rad QX200/QX600:
KRAS G12C + G12D, EGFR L858R + T790M, BRAF V600E (competitive WT/MUT hydrolysis probes), and
EML4-ALK fusion v1 E13;A20 (junction probe). Deliverables: 20 oligos (5 SNV assays + 1 fusion) +
10 synthetic gBlock controls, full-panel multiplex QC, isPcr + paralog specificity, gnomAD SNP screen,
QX200/QX600 channel layouts, LoB/LoD plan. Pipeline 01->11; all SNV codon checks + isPcr PASS.

## 2. Key biology decisions (the why)
- **Competitive hydrolysis probe** (FAM mut / HEX WT, same amplicon) is the primary chemistry: gives allele
  fraction MUT/(MUT+WT) in one well + WT doubles as reference. ARMS/LNA-clamp rejected as primary (ARMS = no
  same-colour WT + mis-prime FP risk; clamp destroys the WT reference the brief wants). Both noted as backups.
- **KRAS G12C+G12D share one amplicon + one WT probe** (c.34/c.35, 1 bp apart).
- **EML4-ALK is an mRNA-junction target** -> needs RNA/cDNA input (one-step RT-ddPCR), normalised to a reference
  gene not a WT allele. Genomic breakpoints scatter across EML4 intron13 / ALK intron19, so a fixed-primer
  genomic assay is NOT reliable on cfDNA. Junction built by splicing EML4 exon13 (ENST00000318522) +
  ALK exon20 (ENST00000389048); verified in-frame by translation (...GKGPK|VYRRKHQEL..., ALK kinase = exon20-29).
- **BRAFP1 pseudogene (chrX, ~95% id over the primer window) co-amplifies BRAF** -> would dilute V600E AF. Fixed by
  anchoring the BRAF reverse-primer 3' end on a BRAF-specific base: a SINGLE 3'-terminal A:G mismatch vs BRAFP1
  (rev 20/21 match, fwd is a perfect match -> discrimination rests on the one terminal mismatch; A:G is a strong
  extension-abolishing type). isPcr confirms single chr7 product. Cost: weak AT 3' end on that primer (flagged, rain
  risk). NOTE: earlier wording said "double" mismatch -- that was wrong (mm_in_3prime4=1 in oligos.json); validate
  against BRAFP1 gDNA at the bench.
- **rs1050171 (EGFR Q787Q, gnomAD AF 0.56) under the T790M probe** -> NOT shifted off (kills discrimination,
  min-dTm dropped to 2.8); instead a **degenerate Y** placed at that probe base (worst-case match Tm ~60-63 >
  55 anneal). Discrimination preserved (min-dTm 5.6).
- 3 probes flagged for 1 LNA at the variant base (min-dTm < 7): KRAS G12D (6.2), EGFR T790M (5.6), BRAF V600E (5.5).
- Multiplex verdict WARN (honest): no inter-assay primer-primer 3'-end dimer <= -5 (worst -4.8); flags are probe
  cross-talk -5..-5.6, EGFR L858R GGCC-palindrome probe self-structure (~-11, locus-intrinsic), KRAS GC-rich hairpins.

## 3. Where the knobs live (code-knob map)
- Targets / HGVS / strand: `01_fetch_sequence.py` SNVS[] (~L35), FUSION{} (~L60), PSEUDOGENES{} BRAFP1 coords.
- Window flank (90 bp): `01` WIN. gnomAD common-SNP cut (1%): `01` COMMON_MAF; fetch via `design_common.gnomad_region`.
- ddPCR buffer (Mg 3.8 / dNTP 0.8 / mono 50): `design_common.py` BUFFER. Tm engine = primer3 NN SantaLucia.
- primer3 globals (Tm 57-63, size 18-25, GC clamp, product size): `03_design_oligos.py` PRIMER_GLOBAL + the
  per-target `sizes` lists. Probe search (len 13-20, SNV 5'-pos 4-8, strand both): `snv_probe_window`.
- LNA threshold (min-dTm < 7): `03` LNA_THRESHOLD. Degenerate handling: `03` degenerate_for_common_snps.
- BRAFP1 anchoring logic: `03` disc_positions + the `attempts` loop (forward G/C anchor preferred, reverse fallback).
- Multiplex dimer thresholds (-5 warn / -7 fail): `05_multiplex_qc.py` END_WARN/END_FAIL.
- Specificity: `06_specificity.py` PAIRS[], PARALOGS[]; isPcr = ~/genomes/isPcr + hg38.2bit.
- Channel dyes / 2-well split: `08_panel_and_controls.py` QX600 + two_well_split (greedy 2-colour on flagged pairs).
- gBlock flank (35 bp): `08` build_gblocks FLANK.
- Report text/sections: `11_build_report.py`. Figures: `09_figures.py`.

## 4. What was tried and didn't work
- Amplicon <= 80 bp for SNVs: left no probe-window interior -> reverted to 70-90 (got 86-89). Don't re-tighten.
- BRAF forward-anchor at idx41/82 (G clamp + discriminate BRAFP1): idx82 too close to V600E (probe Tm 42, dead);
  idx41 squeezed probe. Winning path = reverse-anchor idx119 (double mismatch) + prefer forward GC-clamp pair.
- Ensembl /overlap and POST /variation both return null minor_allele_freq -> useless for MAF. Use gnomAD GraphQL
  (`design_common.gnomad_region`, dataset gnomad_r4). This is THE gotcha that hid rs1050171 on the first pass.

## 5. Gotchas
- gnomAD AF lives in genome{af}/exome{af}; take max. Variant_id = "chrom-pos-ref-alt" (1-based).
- variant_recoder: use the **SPDI** field (0-based plus-strand), not vcf_string (absent); LRG SPDI must be skipped.
- twoBitToFa is 0-based half-open; SPDI pos is 0-based -> consistent (no off-by-one if you keep both 0-based).
- IUPAC probe bases (degenerate Y) break primer3 calc_* -> `design_common.concrete()` resolves to ACGT for QC;
  `rc()` is IUPAC-aware. Registry stores the IUPAC seq for ordering.
- isPcr indexes hg38.2bit on each run (~slow, >120 s) -> runs may background; use -minPerfect=15.
- BRAF & EGFR L858R loci are AT-rich/structured -> low probe Tm / self-dimer are locus-intrinsic, not bugs.
- RUN ORDER is NOT pure numeric: 12_re_compat.py runs AFTER 09 but BEFORE 10/11 (both read re_compat.json).
  Full order: 01 02 03 04 05 06 07 08 09 12 10 11.
- 04_build_registry stores the reference-allele match Tm as the primary `tm` (degenerate probes also carry
  `tm_worstcase` for the rs1050171-alt case, disclosed in report Section 7) -- do not revert to worstcase.
- Report figure letters follow APPEARANCE order: A1-A6 schematics, B=discrimination, C=multiplex matrix,
  D=channel layout (file names match: figure_B_allele_discrimination / figure_C_multiplex_matrix / figure_D_channel_layout).
- 06_specificity.py regenerates the isPcr/BLAST intermediates (ispcr_*, oligo_blast.xml, oligos_query.fasta,
  paralog_cdna.fasta.n*) on each run; they were removed at cleanup. paralog_cdna.fasta is cached/reused.

## 6. Re-running for common modifications
- Add/replace a hotspot: add to `01` SNVS[] (RefSeq HGVS), re-run 01->11. Codon check in `02` validates strand/ref.
- Different annealing temp: it is reported text only (`11`); the design targets 55. Re-tune PRIMER_OPT_TM in `03`
  if you move it far.
- Tighter specificity / new pseudogene: add to `01` PSEUDOGENES{}; `03` will auto-anchor a discriminating 3' end.
- Order LNA probes: the 3 flagged (G12D/T790M/V600E) get 1 LNA at the variant base; re-check dTm on IDT.
