AGENT SKILL

igv-reports

Use when the user wants an HTML, clickable, browseable, offline, or emailable viewer of genomic data — phrases like "HTML IGV report", "offline IGV", "self-contained HTML", "clickable viewer", "create_report", "igv-reports", "email this viewer", or any browseable HTML of reads at variants, fusion breakpoints, SV junctions, viral integrations, ChIP peaks, ROIs, or ONT 5mC/5hmC methylation views at promoters/gene bodies/DMRs. Trigger even when the user doesn't say "igv-reports" — giveaway is HTML/clickable/offline plus genomic regions. Also fire on /igv-reports. DO NOT use for static PNG/PDF/SVG IGV screenshots — use the igv-screenshots skill instead.

License 许可
MIT
最近更新
2026-05-21
Stars
★ 0
Forks
0

01 前言 Front Matter

name
igv-reports
description
Use when the user wants an HTML, clickable, browseable, offline, or emailable viewer of genomic data — phrases like "HTML IGV report", "offline IGV", "self-contained HTML", "clickable viewer", "create_report", "igv-reports", "email this viewer", or any browseable HTML of reads at variants, fusion breakpoints, SV junctions, viral integrations, ChIP peaks, ROIs, or ONT 5mC/5hmC methylation views at promoters/gene bodies/DMRs. Trigger even when the user doesn't say "igv-reports" — giveaway is HTML/clickable/offline plus genomic regions. Also fire on /igv-reports. DO NOT use for static PNG/PDF/SVG IGV screenshots — use the igv-screenshots skill instead.

02 资源文件 66

03 SKILL.md 正文

igv-reports

This skill builds self-contained HTML genomic-region reports with igv-reports (create_report). Each report is a single browseable HTML containing the igv.js viewer plus embedded data slices for every region. No server, no internet, no IGV install needed at view time.

The skill has three entry points:

  • build — one-shot: sites BED + BAM(s) ± VCF → HTML.
  • cohort — multi-sample driver from a samplesheet → per-sample HTMLs + index.
  • prep-track — utility: convert plain-gzip GFF/GTF/BED.gz into a bgzip + tabix-indexed track that igv-reports can load.

What this skill is (and is not)

This skill is a driver layer on top of the upstream igv-reports Python package by the IGV team (github.com/igvteam/igv-reports). The naming is unavoidable — both share the igv-reports name.

Component Source Role
create_report CLI upstream PyPI package igv-reports does the actual HTML rendering
scripts/build_igvreports.py this skill wraps create_report with default-track resolution, cohort/samplesheet mode, SIF auto-detect
scripts/verify_{report,cohort,anchors}.py this skill post-render structural + content audits (not in upstream)
scripts/prep_track.sh this skill bgzip+tabix utility for annotation tracks

The skill is not on PyPI — it's a directory of scripts. Use it by either cloning this repo or copying scripts/ next to your data.

Off-MSKCC quickstart

Defaults assume MSKCC HPC (lab SIF, databases_config.yaml, /data1/greenbab bind). Anywhere else:

# 1. Install the UPSTREAM igv-reports package (provides `create_report`).
#    Use -U to pick up the latest fixes; skill requires >=1.16.0.
pip install -U 'igv-reports>=1.16.0'

# 2. Get this skill's wrapper scripts (one of):
#    - clone:  git clone https://github.com/sahuno/igv-reports-skill.git
#              cd igv-reports-skill
#    - or copy scripts/ next to your project

# 3. Run the wrapper, bypassing the lab databases YAML with explicit paths:
python scripts/build_igvreports.py \
    --genome hg38 \
    --sites sites.hg38.bed \
    --bam tumor.bam normal.bam \
    --fasta /path/to/hg38.fa \
    --no-default-tracks \
    --extra-track /path/to/your_cpg_islands.bed.gz \
    --extra-track /path/to/gencode.v47.annotation.gff3.gz \
    --output report.hg38.html

If you only need raw create_report (no cohort mode, no verifiers, no auto-tracks), skip the skill entirely and use upstream directly — see igvteam/igv-reports docs.

Environment overrides (all optional):

Var Effect
IGV_REPORTS_DB_CONFIG Path to your own databases YAML (same schema as the lab's)
IGV_REPORTS_SIF Path to your own igv-reports apptainer SIF
SAMTOOLS_SIF_DEFAULT Path to your own samtools SIF (verifier only)
IGV_REPORTS_BIND Colon-separated bind paths for singularity (default /data1/greenbab). Empty string disables binding.

Driver flags --fasta and --no-default-tracks let you skip the databases YAML entirely without setting any env var. --no-apptainer forces the PATH create_report path even on a SLURM node. The hermetic tests/unit/ suite runs anywhere with pytest + Python ≥ 3.10.

When to use which entry point

User request Entry point
"Make an HTML for these 5 SV breakpoints in tumor.bam" build
"Give me one HTML per patient for the cohort integration calls" cohort
"create_report fails with 'not BGZF' on this gencode" prep-track

Defaults (locked in)

  • Tracks always loaded, top-to-bottom in the viewer:
    1. CpG islands (BED, plain or bgzipped)
    2. Gencode full annotation (GFF3.gz, transcripts + exons + CDS + UTRs, NOT a gene-level-only file)
    3. RepeatMasker (BED.gz, bgzipped + tabix-indexed) Plus the user's BAM(s), VCF, and any extra tracks they pass.
  • --flanking 300 bp on either side of each site (good for SV breakpoints and point variants alike). Override per call if needed.
  • --standalone so the HTML is offline-viewable.
  • Output filename includes the genome tag — e.g. cohort.hg38.html — to pass enforce-genome-tag.sh.
  • Reference FASTA is resolved from databases_config.yaml: /data1/greenbab/users/ahunos/apps/llm_configs/claude/profiles/databases/databases_config.yaml (lab default; override with $IGV_REPORTS_DB_CONFIG or --no-default-tracks + --fasta). Supported genome IDs: hg38, mm10, mm39, t2t_CHM13v2_plusY, GRCh37.
  • Per-genome default track availability is recorded in references/databases_config_paths.md — read it before assembling tracks so the skill doesn't try to load a track that doesn't exist for the selected genome (e.g., mm39 has no rmsk in our database).

Sites BED format (critical)

igv-reports' BED parser reads fields by position and trips on a header row (ValueError: invalid literal for int() with base 10: 'start'). Always emit a plain headerless 4-column BED:

chr    start    end    name
chr2   25227855 25342590 DNMT3A_full_gene

Tab-separated. The name becomes the row label in the report's variant table — make it specific enough to identify the site after deduping.

By default create_report shows only the chr/start/end position columns in the clickable table. To surface the name (or any extra columns from a 5+ column BED), pass --info-columns <colname> to the driver:

python scripts/build_igvreports.py ... --info-columns name
python scripts/build_igvreports.py ... --info-columns gene_name,score

Column names are matched by header (so a #chrom\tstart\tend\tname\tscore header works). For positional BED without a header, the convention is the 4th column = name, 5th = score, 6th = strand.

The project's enforce-genome-tag.sh hook requires a genome tag in the BED filename: use sites.hg38.bed, not sites.bed.

--type for BED-style sites

When the sites input is a BED (not a VCF), pass --type mutation to create_report (or the driver). This gives the right viewer behavior at each row — one locus per row, no split-screen, table on top. Without it, some BED layouts trigger create_report's split-screen junction view by heuristic. Use --type variant for VCF sites, or omit for create_report's auto-detection (only safe with a VCF).

python scripts/build_igvreports.py ... --type mutation --info-columns name

Pitfalls (the skill should encode and/or detect these)

Symptom Root cause Fix
ValueError: invalid literal for int() on first row Header row in sites BED Strip header — plain BED
UnicodeDecodeError: byte 0x8b reading a track igv-reports reading bgzip as text Filename must end .gff3.gz / .bed.gz AND be true bgzip (check with file <name> for "extra field")
tabix: not BGZF Track was plain-gzipped, not bgzipped Run prep-track entry point
tabix: out of order while indexing GFF/GTF/BED records not pos-sorted within chr prep-track does sort -k1,1 -k4,4n before bgzip
Annotation track empty in viewer Tabix returns no rows in displayed window — often correct biology (e.g., CGI-distal site). Confirm with tabix file region
Genome ID lookup fails with --genome hg38 igv.js bundled IDs require internet at view + render time. Use --fasta /path/to/local.fa instead (always works offline)

Full pitfalls + create_report flag reference in references/best_practices.md.

How to run — quick recipe

Activate the snakemake conda env first; create_report lives there:

source /home/ahunos/miniforge3/etc/profile.d/conda.sh
conda activate snakemake

Then call the bundled driver script (use scripts/ relative to the repo root, or an absolute path to wherever the skill is checked out):

python scripts/build_igvreports.py \
    --sites results/run/inputs/sites.hg38.bed \
    --bam tumor.bam normal.bam \
    --vcf calls.vcf \
    --genome hg38 \
    --output results/run/reports/cohort.hg38.html

The driver:

  • Resolves the genome's CpG / gencode / rmsk paths from databases_config.yaml (skipping any that don't exist for the chosen genome).
  • Validates the sites BED is headerless and that all rows have start < end.
  • Calls create_report with --flanking 300 --standalone.
  • Writes a logs/ entry capturing the full command, the flanking value, the per-region embedded data sizes, and the resolved track list — required by the project's analysis-script audit-trail expectations.

For multi-sample cohorts, use --samplesheet samplesheet.tsv instead of --bam/--vcf. Samplesheet format: sample, bam_tumor, bam_normal, vcf, sites_bed. The driver emits one HTML per sample plus a top-level index.html that lists all samples with links. Pass --jobs N to build the per-sample HTMLs in parallel via ThreadPoolExecutor (each create_report call is I/O-bound on BAM slicing, so threading scales well; --jobs 6 for a 6-patient cohort roughly 1/Nx wall-clock vs sequential). Default is --jobs 1. Layout matches the ATLL viral-integration reference implementation:

results/<run>/
├── inputs/<sample>/sites.<genome>.bed
├── reports/<sample>.<genome>.html
├── reports/index.html
└── logs/run_<timestamp>.log

prep-track — fixing a non-bgzip track

If a GFF3/GTF/BED.gz is plain-gzip rather than bgzip, igv-reports fails silently or with an obscure error. Two modes:

In-place (with .bak.original_gzip backup) — replaces the original:

bash scripts/prep_track.sh /path/to/track.gff3.gz

Sibling file (non-destructive — original untouched) — write the bgzipped+indexed track to a new path. Use this when other pipelines point at the original .gff3.gz and you can't risk a brief window where the file is replaced:

bash scripts/prep_track.sh /path/to/track.gff3.gz \
    --out /path/to/track.bgz.gff3.gz

The script (both modes):

  1. Backs up the original to <name>.bak.original_gzip (in-place mode only).
  2. gunzip -cs the file.
  3. Sorts by chr then numeric pos (sort -k1,1 -k4,4n). (Gencode delivers records interleaved by feature type at the same locus — tabix requires pos-sorted.)
  4. bgzips to target.
  5. tabix -p <gff|gtf|bed>s.
  6. Verifies a sample tabix query returns rows.

Run from any env that has htslib's bgzip + tabix on PATH.

Diagnostic — file <name> for distinguishing the two formats:

  • Plain gzip: gzip compressed data, from Unix, original size <N>
  • bgzip: Blocked GNU Zip Format (BGZF; gzipped file with extra field)

The extra field keyword is the bgzip giveaway.

When generating an answer.md / run.sh for the user

The driver script (build_igvreports.py) deliberately abstracts the underlying create_report flags — it sets --standalone, --fasta, the --flanking 300 default, and the YAML-resolved annotation tracks internally so the user doesn't have to remember them. That abstraction is good for ergonomics but bad for auditability: a reviewer reading the answer.md later can't see what flags are actually being invoked without opening the driver source.

To keep both: when you produce a runnable command for the user, also include a code block titled "Equivalent direct create_report invocation" that shows the fully-expanded command with all flags and resolved track paths inline. The user should see the wrapper command they're going to run AND the underlying command it expands to. Example:

## Run

```bash
python build_igvreports.py --genome mm10 --sites peaks.mm10.bed \\
    --bam ./data/ip.bam ./data/input.bam \\
    --output reports/peaks_qc.mm10.html
```

### Equivalent direct create_report invocation

```bash
create_report peaks.mm10.bed \\
    --fasta /data1/greenbab/database/mm10/mm10.fa \\
    --flanking 300 --standalone \\
    --tracks ./data/ip.bam ./data/input.bam \\
        /data1/greenbab/database/mm10/mm10_CpGIslands.bed \\
        /data1/greenbab/database/mm10/annotations/gencode.vM25.annotation.gtf.gz \\
        /data1/greenbab/database/RepeatMaskerDB/.../rmsk_all_repeats_mm10.bed.gz \\
    --title "ChIP-seq peak QC (mm10) — IP vs Input" \\
    --output reports/peaks_qc.mm10.html
```

This costs you ~10 lines and gives the reviewer a full audit trail. For cohort runs, show the expanded form for ONE representative sample only — the others differ only in BAM/VCF paths.

Post-render verification

scripts/verify_report.py parses a built HTML and confirms it actually contains what its inputs declared. Six checks: html_exists, html_min_size, region_count (tableJson rows == sites BED rows), region_coords (each BED row finds a matching (chrom, start+1, end[, name]) in tableJson — BED is 0-based, the HTML stores 1-based start), region_sessions (sessionDictionary has one entry per row), and tracks_present (every name from --track-config or every basename from positional --tracks appears in the decoded igv.js session's tracks[].name list).

python scripts/verify_report.py \
    --html         results/<run>/reports/sample.hg38.html \
    --sites        results/<run>/inputs/sites.hg38.bed \
    --track-config results/<run>/inputs/tracks.json \
    --min-size-mb  1.0 \
    --out          results/<run>/reports/sample.verify.tsv \
    --fail-on-fail

Output is a TSV with columns check / status / observed / expected / details (also printed to stdout). With --fail-on-fail, exits nonzero if any check is FAIL — wire this into Snakemake / CI so the pipeline gates on render quality, not just on create_report's exit code.

NOTE: --standalone replaces every track URL with an inlined data: URL after slicing, so URL paths are unrecoverable from the embedded session. The check matches on track NAMES (which --standalone preserves) — for --track-config JSON pass meaningful names; positional --tracks mode uses basenames.

Cohort-level verification (verify_cohort.py)

The per-sample verifier above confirms each HTML is internally consistent but cannot tell whether sample-1's HTML accidentally embeds sample-2's BAM (e.g., samplesheet typo, copy-paste, tumor/normal slot swap). For cohort runs, scripts/verify_cohort.py adds five cross-sample checks:

Check What it asserts
cohort_html_coverage (global) Each samplesheet row has exactly one HTML; flags missing + extras
sample_tracks_match (per-sample) Each HTML's session contains every BAM/VCF basename declared in THAT row
no_cross_sample_contamination (per-sample) Each HTML contains no basename that belongs to a DIFFERENT row's track columns (default tracks from databases_config.yaml are allow-listed)
sample_id_embedded (per-sample) The sample column value appears in the HTML's <title> or filename
index_consistency (global) index.html links exactly the samplesheet sample set; each target exists and is non-empty

Auto-invoked by default at the end of build_igvreports.py --samplesheet cohort runs. Disable with --no-verify; gate the pipeline with --fail-on-fail. Standalone invocation:

python scripts/verify_cohort.py \
    --samplesheet samplesheet.tsv \
    --reports-dir results/<run>/reports/ \
    --genome hg38 \
    --out results/<run>/reports/cohort_verify.tsv \
    --summary results/<run>/reports/cohort_verify.summary.md \
    --fail-on-fail

The TSV adds a sample column on top of the per-sample verify schema, with "*" for cohort-global rows. The markdown rollup (--summary) groups PASS/FAIL counts by check + lists every failure inline.

Worked regression: tests/integration/cohort_verify/scenarios.sh builds a 3-sample cohort and asserts each of four corruption scenarios (missing HTML, sample swap, index drift, truncated HTML) triggers the expected check FAILs.

Content verification (verify_anchors.py) — opt-in, slow

verify_cohort.py proves the HTML says the right thing. It can NOT confirm the embedded BAM slice contains the data it claims to. Two failure modes slip past structural checks:

  1. Sample swap with matching basename — the cohort loop wired the wrong BAM into sample_1's build, but the swapped BAM's Path.stem happens to match what sample_1's row declared (or two files in different dirs share a basename). Track name passes; slice content is wrong.
  2. Silent empty slice — region rendered, but the slice has 0 reads (failed samtools index, source BAM corruption, coords outside coverage).

scripts/verify_anchors.py closes the gap by re-running samtools view -c against both the source BAM (at generate time) and the embedded slice (at verify time), then comparing counts. Two-mode workflow:

# 1. After the cohort renders cleanly, freeze the read counts as a regression fixture.
python scripts/verify_anchors.py generate \
    --samplesheet samplesheet.tsv \
    --sites sites.hg38.bed \
    --out anchors.hg38.tsv

# 2. Re-verify any time after — works against a fresh build of the same inputs,
#    or to audit an existing HTML for unexpected content drift.
python scripts/verify_anchors.py verify-cohort \
    --samplesheet samplesheet.tsv \
    --reports-dir results/<run>/reports/ \
    --genome hg38 \
    --anchors anchors.hg38.tsv \
    --out results/<run>/reports/cohort_verify_anchors.tsv \
    --fail-on-fail

Or chained into the build driver:

# Freeze anchors at build time:
python scripts/build_igvreports.py --samplesheet ... --anchors-mode generate \
    --anchors anchors.hg38.tsv

# Verify a later build against frozen anchors:
python scripts/build_igvreports.py --samplesheet ... --anchors-mode verify \
    --anchors anchors.hg38.tsv --fail-on-fail

Anchors TSV schema (#-prefixed header per lab BED convention):

#sample	track_name	track_type	chrom	start	end	expected	tolerance	min	max	notes

track_type is one of:

  • bam — expected is the count from samtools view -c -F 1536 against the source BAM at generate time, and the same count against the embedded BAM slice at verify time. Default when the column is absent (backwards compat — pre-2026-05-19 anchor files keep working).
  • bedgraph — expected is the number of data rows in the source bedGraph overlapping the region (CpG count for methylation data, peak count for ChIP coverage). Verify-time count comes from the wig/bedGraph slice embedded by igv-reports in the HTML — gzip-decoded in-memory, no samtools needed.

bedGraph tracks come from the samplesheet's extra_tracks column. Anchors for them are generated automatically alongside BAM anchors when you run verify_anchors.py generate against a samplesheet that includes bedGraph entries (e.g. *.5mC.bedgraph, *.5hmC.bg, plain or .gz).

tolerance is a ratio (default 5%). min/max are absolute bounds that override tolerance when set — useful for known-positive sites like "this integration must have ≥20 reads" or "this promoter must have ≥10 CpGs".

samtools is resolved in this order: --samtools-sif PATH → $SAMTOOLS_SIF → /data1/greenbab/users/ahunos/apps/containers/samtools_v1.23.1.sif → PATH samtools. SIF preferred per rules/apptainer_vs_conda.md. bedGraph anchors don't require samtools.

Why this matters for methylation viewers: the silent-failure mode for methylation reports is "region rendered, slice has 0 CpGs" — an empty bedGraph slice because the source had no calls in that window, or because the slice extraction silently dropped them. Pure structural verification (verify_cohort.py) confirms the bedGraph track is in the HTML but can't tell whether it's empty. The bedgraph-anchor mode closes this gap.

Why opt-in and not default: the verify step shells out to samtools per (sample × region) and indexes each slice — ~1 s/anchor. For a 6-sample cohort × 50 regions that's ~5 min on top of the structural verify (which runs in seconds). Reach for this when sample swap or content regression is a real concern; the structural verifier is sufficient for routine builds.

Worked regression: tests/integration/anchor_verify/scenarios.sh builds a 2-sample cohort and asserts each of four content scenarios (tolerance violation, min-bound violation, corrupted slice, missing anchor) triggers the expected PASS / FAIL / SKIP outcome.

Triage interpretation (interpret_reports.py) — human-first reading surface

After verify-cohort produces a checks TSV, interpret_reports.py rolls it up into a single cohort-wide interpretation.md: a summary table (sample × verdict counts) then per-sample sections with a per-region verdict.

python scripts/interpret_reports.py \
    --checks results/<run>/reports/cohort_verify_anchors.tsv \
    --out    results/<run>/reports/interpretation.md

Per-region verdict, derived solely from the anchor results (no new thresholds):

  • PASS — every non-SKIP anchor for the region passed
  • FAIL — every non-SKIP anchor failed
  • REVIEW — mixed pass/fail across the region's tracks
  • UNVERIFIED — every anchor was SKIP (region not rendered, or no tracks matched) — distinct from FAIL: nothing was checked

Regions sort FAIL-first (FAIL → REVIEW → UNVERIFIED → PASS) so the ones needing attention sit on top of each sample section; FAIL/REVIEW/UNVERIFIED get a per-track breakdown (observed vs expected + the reason), PASS collapses to one line. If the checks TSV is missing or empty, a fallback file points the reader at the HTML instead. It's a reporting tool — it always exits 0 on a successful write; verify_anchors --fail-on-fail remains the CI gate.

Worked regression: tests/integration/interpret/scenarios.sh renders a synthetic cohort and asserts the summary totals, FAIL-first ordering, the malformed-input exit-2 path, and the missing-input fallback.

Output and workflow logging

Every run logs to logs/run_<YYYYMMDD_HHMMSS>.log next to the reports dir. The log captures:

  • Resolved track paths (per genome, after databases_config.yaml lookup).
  • The exact create_report command.
  • The flanking value used (default 300 bp — this is the value that's baked into all the embedded data slices, so audit trails depend on it).
  • Per-region embedded data sizes (extracted post-render so the user can see which regions inflated the HTML).
  • Total HTML size.

This satisfies CLAUDE.md §"Logging and Audit Trail" — every run is reproducible from the log alone.

Track choice nuances

For gencode on hg38, the default points at gencode.v47.annotation.gff3.gz (full annotation, bgzip + tabix). This gives transcript models with exons / CDS / UTRs. The gene-level-only companion (gencode.v47.genes.annotation.sorted.gff3.gz) renders only solid gene boxes and is fine for high-zoom views, but the full annotation is the right default for read-level inspection at integration / fusion / SV junctions.

For mouse genomes, databases_config.yaml ships .gtf.gz paths instead. GTFs work in igv-reports if bgzip + tabix-indexed; prep-track converts plain-gzip GTFs the same way it does GFF3s.

For T2T-CHM13, only the FASTA + GTF + CGI are indexed in our DB; rmsk is absent and is auto-skipped by the driver. The variant table will load without rmsk; flag this in the run log.

Common-case examples

The examples/ directory has runnable templates:

  • single_sample.sh — one BAM + one VCF + a sites BED → one HTML.
  • cohort_samplesheet.sh — TSV-driven multi-sample run.
  • prep_track_demo.sh — convert a plain-gzip gencode to bgzip+tabix.
  • methylation_ont/ — ONT 5mC/5hmC viewer (BAM with colorBy: basemod2
    • per-sample bedGraph at fixed y-axis 0..100). End-to-end worked example with pre-sliced data; recipe.md explains the slots.

These are reference implementations; copy and edit them for new runs rather than starting from scratch.

Tests

Three-layer suite under tests/, orchestrated by tests/run_all.sh:

Layer What it covers Runtime Needs
unit (tests/unit/) parser layer of verify_report.py + verify_anchors.py — TSV loading, status decision, session-entry locator, balanced-brace JSON extractor, decode round-trip — all with synthetic inputs ~1 s pytest
smoke (tests/smoke/) samtools_count / samtools_index / full slice-decode-and-count round-trip against the committed tests/fixtures/tiny_colo829.hg38.bam (457 KB, sliced from public ONT COLO829 release) ~3 s pytest + samtools (SIF or PATH)
integration (tests/integration/) end-to-end: build a 2-/3-sample cohort, structural verify, anchor verify, run 4 corruption scenarios per verifier ~7 min cold, ~30 s cached full cohort BAMs (lab default OR IGV_REPORTS_TEST_BAM_{1,2,3} env override). SKIPs with exit 77 if neither is available
bash tests/run_all.sh                  # all three layers
bash tests/run_all.sh --unit-only      # ~1 s — fastest feedback loop
bash tests/run_all.sh --no-integration # ~12 s — works on any machine
bash tests/run_all.sh --integration-only

The fixture provenance + regeneration recipe live in tests/fixtures/README.md. Anchor counts the smoke layer expects (chr2=5, chr7=9) are the contract — any fixture regeneration that changes them must also update the smoke test constants.

ONT methylation viewers (specialized path)

For per-read 5mC/5hmC visualization the positional --tracks API does not work — you need named tracks with colorBy: "basemod2" on the BAMs and min: 0, max: 100 on the bedGraph tracks (cross-sample y-axis lock, see rules/igv.md). Use the --track-config <json> passthrough:

# 1. Write a YAML spec listing samples (see tracks_spec.example.yaml).
# 2. Generate tracks.json with the right defaults baked in:
python scripts/generate_tracks_json.py \
    --spec tracks_spec.yaml --run-dir results/<run>/ \
    --out results/<run>/tracks.json

# 3. Build the report:
python scripts/build_igvreports.py \
    --sites results/<run>/sites.hg38.bed \
    --track-config results/<run>/tracks.json \
    --genome hg38 --flanking 0 \
    --type mutation --info-columns name \
    --output results/<run>/methylation_report.hg38.html

Annotation shortcuts in the YAML

The default --tracks path (SV/variant viewers) auto-resolves CpG islands, gencode, and RepeatMasker from databases_config.yaml when you pass --genome hg38. On the --track-config (methylation) path you used to have to hand-paste those paths into the YAML. As of the methylation-polish round, you can use a default: shortcut for the same resolution:

genome: hg38

annotation:
  # SHORTCUT — resolved from databases_config.yaml for the genome above.
  # Gets an Okabe-Ito color + sensible displayMode you can override per entry.
  - default: gencode
  - default: cgi
  - default: repmasker
  - default: epdnew_coding         # hg38 only
  - default: epdnew_noncoding      # hg38 only

  # Mix with EXPLICIT entries when needed (e.g. a pre-sliced custom track):
  - name: "My custom peak set"
    url: peaks/promoter_slices.bed
    format: bed

Valid default: keys: cgi, gencode, repmasker, epdnew_coding, epdnew_noncoding. Mixing both forms is supported; order is preserved. Override the canned name/color/displayMode per entry by adding the field alongside default:. The shortcut needs a top-level genome: in the spec and reads databases_config.yaml (resolution path same as the SV viewer driver — points at $IGV_REPORTS_DB_CONFIG if set, else the lab default; override per-run with --db-config PATH).

Key methylation-specific defaults:

  • --flanking 0 (sites BED already encodes the window — promoter/gene span).
  • --info-columns name (surface the BED name column in the variant table).
  • --type mutation (one-locus view per row; not split-screen).
  • bedGraph not bigwig — igv-reports cannot slice .bw directly.

When --track-config is set the driver bypasses the auto-resolved default annotation tracks (CGI / gencode / rmsk) and the --bam / --vcf / --extra-track flags — the JSON is the source of truth. Build annotation slices into the JSON instead.

--apptainer is auto-detected: the driver flips to the dedicated igv-reports 1.16.0 SIF (/data1/greenbab/users/ahunos/apps/containers/igv-reports_1.16.0.sif, 83 MB, pulled from Galaxy depot) when SLURM_JOB_ID is in the environment — i.e. running on a compute node where the NFS conda cold-start tax matters (rules/apptainer_vs_conda.md). On the login node it stays on the conda env. Override with --apptainer / --no-apptainer; the decision lands in the run log.

Full recipe and rationale: references/methylation_ont.md. Worked example with real data: examples/methylation_ont/.

Exporting HTML and PNG side-by-side (--also-png)

The HTML report is the deep-dive view; sometimes you also need static PNGs you can email, drop in a Slack channel, or paste into slides. The driver's --also-png flag invokes the sister igver skill against the same sites BED and same track list that drove create_report, so both artifacts cover identical regions with matching content.

python scripts/build_igvreports.py \
    --samplesheet samplesheet.tsv \
    --genome hg38 \
    --output-dir results/run/reports/ \
    --jobs 6 \
    --also-png \
    --png-dpi 600 --png-display-mode collapse

Output layout per sample:

results/run/reports/
├── <sample>.hg38.html              # interactive
├── png_<sample>.hg38/
│   ├── igver_regions.bed           # flanked BED with UIDs (igver -r)
│   ├── igver_input.txt             # track paths, one per line (igver -i)
│   ├── manifest.tsv                # bridge: BED row ↔ PNG ↔ HTML row
│   └── png/
│       ├── chr1-100-500.alpha.png  # one PNG per region
│       └── chr2-0-700.beta.png
└── index.html

How consistency is guaranteed — five levers

  1. Single sites BED with --flanking baked in. The driver writes igver_regions.bed with start − flanking and end + flanking already applied (clamped to 0 on the low side); igver sees the same coordinates create_report's igv.js viewer slices to.
  2. Single resolved track list. On the default (positional) path the exact [BAMs, VCF, extras, defaults] list passed to create_report is also written to igver_input.txt. On the --track-config path the local-path url: entries from the JSON are extracted (http(s) URLs are skipped — igver can't consume them).
  3. Matched display mode. Default is --png-display-mode collapse to line up with the HTML's BAM_DEFAULTS displayMode: COLLAPSED. Override to expand for per-read SV inspection on both artifacts.
  4. UID-based filenames. The BED's name column (auto-assigned region_<idx> when missing) becomes both the HTML table label (via --info-columns name) and the PNG filename suffix (<chr-start-end>.<uid>.png). A user finds the same region in either artifact by the same string.
  5. manifest.tsv audit trail. Per-sample TSV with columns: bed_row_idx, uid, chrom, start_orig, end_orig, start_flanked, end_flanked, region, png_path, html_path, html_table_row. One row per region in BED order. verify_cohort.py reads this to run three PNG-side checks (count matches, exist + non-empty, html-row contiguity).

Resolution of the igver invocation

Order, first match wins:

  1. --igver-cmd '...' (split on whitespace — supports apptainer exec ... igver).
  2. $IGVER_CMD env var (same shape).
  3. igver on PATH.
  4. Lab apptainer SIF at /data1/greenbab/software/images/igver_latest.sif (only if reachable).

If none resolve, the build exits before invoking create_report so you don't pay the HTML cost before finding out PNGs are unavailable.

Methylation caveat (bigwig vs bedGraph)

The HTML methylation path uses bedGraph tracks (igv.js consumes those directly); igver's per-read methylation view uses BAMs with --color-by BASE_MODIFICATION, and igver's cross-sample comparison view uses bigwig tracks. Content can be made identical only if both formats trace back to the same modkit pileup output (modkit bedmethyl tobigwig of the same bedGraph). The driver's --also-png passes the JSON's url: entries through verbatim, so if your YAML lists bedGraphs they'll go to igver as-is — igver will render them but the result may look different from the HTML's color-coded per-read view. For publication-quality methylation PNGs, supply a parallel tracks.json that lists bigwigs and run igver separately (see rules/igv.md).

For SV/variant viewers this caveat doesn't apply — both render the identical BAMs and the result is content-equivalent.

Cross-artifact verification

The driver runs an inline existence check right after igver returns: walks each expected PNG path (<chr>-<start>-<end>.<uid>.<ext> derived from the manifest) and fails the build with an actionable message if any are missing or zero-byte. This catches igver's documented silent-exit-0 failure mode (egg-link install without the IGV Java binary; see rules/igv.md) — proc.returncode != 0 alone misses it.

In addition, verify_cohort.py then runs three checks per sample:

Check Catches
png_count_matches_bed partial igver run (SIGKILL mid-batch), stale manifest from a previous build, filename collisions
pngs_exist_and_nonempty empty IGV screenshots (< 10 KB threshold; useful screenshots are typically ≥ 50 KB)
png_html_row_alignment manifest rows referencing a different HTML, html_table_row not contiguous 1..N

--png-min-size-kb 5.0 lowers the threshold if you have legitimate no-data regions where igver produces a near-empty PNG.

Credits

The HTML rendering engine here is not this skill — it is the upstream igv-reports package (create_report) maintained by the IGV team at the Broad Institute (MIT, © 2018-2019 The Broad Institute and The Regents of the University of California). The rendered HTML embeds igv.js from the same team. This skill is a driver + verifier layer on top.

When producing outputs that will end up in a publication, cite the igv.js paper: Robinson, J. T., Thorvaldsdóttir, H., Turner, D., & Mesirov, J. P. (2023). igv.js: an embeddable JavaScript implementation of the Integrative Genomics Viewer (IGV). Bioinformatics, 39(1), btac830. doi:10.1093/bioinformatics/btac830.

Full attribution, the upstream MIT license verbatim, the reuse-vs-add table, and BibTeX entries live in CREDITS.md.

See also

  • references/best_practices.md — full create_report flag reference, format gotchas, performance notes. Read this if a run fails in a way not listed in the Pitfalls table above.
  • references/databases_config_paths.md — per-genome track availability matrix and exact YAML keys. Read this when adding a new genome or diagnosing a missing-track warning.
  • references/methylation_ont.md — ONT 5mC/5hmC cheat-sheet (colorBy, min:0/max:100, flanking=0, bedGraph vs bigwig, EPDnew lookup).
  • scripts/build_igvreports.py — the driver. Reads --samplesheet or --bam/--vcf direct-args, resolves tracks, validates the sites BED, writes the HTMLs and the run log. Supports --track-config <json> passthrough for fully-styled track sets.
  • scripts/generate_tracks_json.py — YAML spec → tracks.json with ONT-methylation defaults baked in (colorBy=basemod2, min:0/max:100, group-paired Okabe-Ito colors).
  • scripts/verify_report.py — post-render structural verifier; parses the HTML's embedded tableJson + sessionDictionary, confirms region count / coordinates / track names match the inputs. Emits a verify.tsv and gates on --fail-on-fail.
  • scripts/verify_cohort.py — cohort-level verifier; layered on top of verify_report's per-sample checks, adds cross-sample contamination scanning + index.html / sample-id consistency. Auto-invoked at the end of build_igvreports.py --samplesheet; standalone-runnable too.
  • scripts/verify_anchors.py — content verifier; samtools-counts the embedded BAM slices and compares to anchors frozen from the source BAMs at build time. Catches sample swaps that share basenames and silent empty slices. Opt-in via --anchors-mode generate|verify on the build driver; slow (~1 s/anchor). See SKILL.md content-verification section.
  • scripts/prep_track.sh — gunzip → sort → bgzip → tabix utility.
  • igv-screenshots skill — the static PNG/PDF/SVG counterpart based on igver. Use it instead of this one when the deliverable is a publication-quality figure rather than a clickable viewer.
  • Reference implementation: /data1/greenbab/projects/ont/Project_17424/results/20260503_hg38plusHTLV1EBV_cohort_integration_igvreports/ — 6-patient ATLL cohort viral-integration HTMLs + DNMT3A sanity check; this skill was extracted from that work.

04 同仓库更多 Skills