[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M733WV21R7PHTDZ4CWCYACPX":3,"skill-same:01M733WV21R7PHTDZ4CWCYACPX":317},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":16,"sourceHtmlUrl":17,"categories":18,"keywords":19,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":14,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":31,"updatedAt":32,"install":33,"resources":46},"01M733WV21R7PHTDZ4CWCYACPX","igv-reports",null,"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\u002F5hmC methylation views at promoters\u002Fgene bodies\u002FDMRs. Trigger even when the user doesn't say \"igv-reports\" — giveaway is HTML\u002Fclickable\u002Foffline plus genomic regions. Also fire on \u002Figv-reports. DO NOT use for static PNG\u002FPDF\u002FSVG IGV screenshots — use the igv-screenshots skill instead.","sahuno\u002Figv-reports-skill","sahuno","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F38211023?v=4","01M733WQKMCPYWEQRKGVZTWHBG","Samuel Terkper Ahuno","01M733WQMBBPCMZ4YV5Z0KHK4G",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: igv-reports\ndescription: 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\u002F5hmC methylation views at promoters\u002Fgene bodies\u002FDMRs. Trigger even when the user doesn't say \"igv-reports\" — giveaway is HTML\u002Fclickable\u002Foffline plus genomic regions. Also fire on \u002Figv-reports. DO NOT use for static PNG\u002FPDF\u002FSVG IGV screenshots — use the igv-screenshots skill instead.\n---\n\n# igv-reports\n\nThis skill builds **self-contained HTML genomic-region reports** with\n[igv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) (`create_report`).\nEach report is a single browseable HTML containing the igv.js viewer plus\nembedded data slices for every region. No server, no internet, no IGV\ninstall needed at view time.\n\nThe skill has three entry points:\n- **build** — one-shot: sites BED + BAM(s) ± VCF → HTML.\n- **cohort** — multi-sample driver from a samplesheet → per-sample HTMLs + index.\n- **prep-track** — utility: convert plain-gzip GFF\u002FGTF\u002FBED.gz into a\n  bgzip + tabix-indexed track that igv-reports can load.\n\n## What this skill is (and is not)\n\nThis skill is a **driver layer** on top of the upstream `igv-reports`\nPython package by the IGV team\n([github.com\u002Figvteam\u002Figv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports)).\nThe naming is unavoidable — both share the `igv-reports` name.\n\n| Component | Source | Role |\n|---|---|---|\n| `create_report` CLI | upstream PyPI package `igv-reports` | does the actual HTML rendering |\n| `scripts\u002Fbuild_igvreports.py` | **this skill** | wraps `create_report` with default-track resolution, cohort\u002Fsamplesheet mode, SIF auto-detect |\n| `scripts\u002Fverify_{report,cohort,anchors}.py` | **this skill** | post-render structural + content audits (not in upstream) |\n| `scripts\u002Fprep_track.sh` | **this skill** | bgzip+tabix utility for annotation tracks |\n\nThe skill is not on PyPI — it's a directory of scripts. Use it by either\ncloning this repo or copying `scripts\u002F` next to your data.\n\n## Off-MSKCC quickstart\n\nDefaults assume MSKCC HPC (lab SIF, `databases_config.yaml`, `\u002Fdata1\u002Fgreenbab`\nbind). Anywhere else:\n\n```bash\n# 1. Install the UPSTREAM igv-reports package (provides `create_report`).\n#    Use -U to pick up the latest fixes; skill requires >=1.16.0.\npip install -U 'igv-reports>=1.16.0'\n\n# 2. Get this skill's wrapper scripts (one of):\n#    - clone:  git clone https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill.git\n#              cd igv-reports-skill\n#    - or copy scripts\u002F next to your project\n\n# 3. Run the wrapper, bypassing the lab databases YAML with explicit paths:\npython scripts\u002Fbuild_igvreports.py \\\n    --genome hg38 \\\n    --sites sites.hg38.bed \\\n    --bam tumor.bam normal.bam \\\n    --fasta \u002Fpath\u002Fto\u002Fhg38.fa \\\n    --no-default-tracks \\\n    --extra-track \u002Fpath\u002Fto\u002Fyour_cpg_islands.bed.gz \\\n    --extra-track \u002Fpath\u002Fto\u002Fgencode.v47.annotation.gff3.gz \\\n    --output report.hg38.html\n```\n\nIf you only need raw `create_report` (no cohort mode, no verifiers, no\nauto-tracks), skip the skill entirely and use upstream directly —\nsee [igvteam\u002Figv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) docs.\n\nEnvironment overrides (all optional):\n\n| Var | Effect |\n|---|---|\n| `IGV_REPORTS_DB_CONFIG` | Path to your own databases YAML (same schema as the lab's) |\n| `IGV_REPORTS_SIF` | Path to your own `igv-reports` apptainer SIF |\n| `SAMTOOLS_SIF_DEFAULT` | Path to your own `samtools` SIF (verifier only) |\n| `IGV_REPORTS_BIND` | Colon-separated bind paths for singularity (default `\u002Fdata1\u002Fgreenbab`). Empty string disables binding. |\n\nDriver flags `--fasta` and `--no-default-tracks` let you skip the databases\nYAML entirely without setting any env var. `--no-apptainer` forces the\nPATH `create_report` path even on a SLURM node. The hermetic `tests\u002Funit\u002F`\nsuite runs anywhere with `pytest` + Python ≥ 3.10.\n\n## When to use which entry point\n\n| User request | Entry point |\n|---|---|\n| \"Make an HTML for these 5 SV breakpoints in tumor.bam\" | **build** |\n| \"Give me one HTML per patient for the cohort integration calls\" | **cohort** |\n| \"create_report fails with 'not BGZF' on this gencode\" | **prep-track** |\n\n## Defaults (locked in)\n\n- Tracks always loaded, top-to-bottom in the viewer:\n  1. CpG islands (BED, plain or bgzipped)\n  2. Gencode full annotation (GFF3.gz, **transcripts + exons + CDS + UTRs**, NOT a gene-level-only file)\n  3. RepeatMasker (BED.gz, bgzipped + tabix-indexed)\n  Plus the user's BAM(s), VCF, and any extra tracks they pass.\n- `--flanking 300` bp on either side of each site (good for SV breakpoints\n  and point variants alike). Override per call if needed.\n- `--standalone` so the HTML is offline-viewable.\n- Output filename includes the genome tag — e.g. `cohort.hg38.html` —\n  to pass `enforce-genome-tag.sh`.\n- Reference FASTA is resolved from `databases_config.yaml`:\n  `\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fllm_configs\u002Fclaude\u002Fprofiles\u002Fdatabases\u002Fdatabases_config.yaml`\n  (lab default; override with `$IGV_REPORTS_DB_CONFIG` or `--no-default-tracks` + `--fasta`).\n  Supported genome IDs: `hg38`, `mm10`, `mm39`, `t2t_CHM13v2_plusY`, `GRCh37`.\n- Per-genome default track availability is recorded in\n  `references\u002Fdatabases_config_paths.md` — read it before assembling tracks\n  so the skill doesn't try to load a track that doesn't exist for the\n  selected genome (e.g., mm39 has no rmsk in our database).\n\n## Sites BED format (critical)\n\nigv-reports' BED parser reads fields **by position** and trips on a header\nrow (`ValueError: invalid literal for int() with base 10: 'start'`). Always\nemit a **plain headerless 4-column BED**:\n\n```\nchr    start    end    name\nchr2   25227855 25342590 DNMT3A_full_gene\n```\n\nTab-separated. The `name` becomes the row label in the report's variant\ntable — make it specific enough to identify the site after deduping.\n\nBy default `create_report` shows only the chr\u002Fstart\u002Fend position columns\nin the clickable table. To surface the `name` (or any extra columns from\na 5+ column BED), pass `--info-columns \u003Ccolname>` to the driver:\n\n```bash\npython scripts\u002Fbuild_igvreports.py ... --info-columns name\npython scripts\u002Fbuild_igvreports.py ... --info-columns gene_name,score\n```\n\nColumn names are matched by header (so a `#chrom\\tstart\\tend\\tname\\tscore`\nheader works). For positional BED without a header, the convention is the\n4th column = `name`, 5th = `score`, 6th = `strand`.\n\nThe project's `enforce-genome-tag.sh` hook requires a genome tag in the BED\nfilename: use `sites.hg38.bed`, not `sites.bed`.\n\n### `--type` for BED-style sites\n\nWhen the sites input is a BED (not a VCF), pass `--type mutation` to\n`create_report` (or the driver). This gives the right viewer behavior at\neach row — one locus per row, no split-screen, table on top. Without it,\nsome BED layouts trigger create_report's split-screen junction view by\nheuristic. Use `--type variant` for VCF sites, or omit for create_report's\nauto-detection (only safe with a VCF).\n\n```bash\npython scripts\u002Fbuild_igvreports.py ... --type mutation --info-columns name\n```\n\n## Pitfalls (the skill should encode and\u002For detect these)\n\n| Symptom | Root cause | Fix |\n|---|---|---|\n| `ValueError: invalid literal for int()` on first row | Header row in sites BED | Strip header — plain BED |\n| `UnicodeDecodeError: byte 0x8b` reading a track | igv-reports reading bgzip as text | Filename must end `.gff3.gz` \u002F `.bed.gz` AND be true bgzip (check with `file \u003Cname>` for \"extra field\") |\n| `tabix: not BGZF` | Track was plain-gzipped, not bgzipped | Run **prep-track** entry point |\n| `tabix: out of order` while indexing | GFF\u002FGTF\u002FBED records not pos-sorted within chr | **prep-track** does `sort -k1,1 -k4,4n` before bgzip |\n| 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` |\n| Genome ID lookup fails with `--genome hg38` | igv.js bundled IDs require internet at view + render time. Use `--fasta \u002Fpath\u002Fto\u002Flocal.fa` instead (always works offline) |\n\nFull pitfalls + create_report flag reference in `references\u002Fbest_practices.md`.\n\n## How to run — quick recipe\n\nActivate the snakemake conda env first; `create_report` lives there:\n\n```bash\nsource \u002Fhome\u002Fahunos\u002Fminiforge3\u002Fetc\u002Fprofile.d\u002Fconda.sh\nconda activate snakemake\n```\n\nThen call the bundled driver script (use `scripts\u002F` relative to the repo\nroot, or an absolute path to wherever the skill is checked out):\n\n```bash\npython scripts\u002Fbuild_igvreports.py \\\n    --sites results\u002Frun\u002Finputs\u002Fsites.hg38.bed \\\n    --bam tumor.bam normal.bam \\\n    --vcf calls.vcf \\\n    --genome hg38 \\\n    --output results\u002Frun\u002Freports\u002Fcohort.hg38.html\n```\n\nThe driver:\n- Resolves the genome's CpG \u002F gencode \u002F rmsk paths from `databases_config.yaml`\n  (skipping any that don't exist for the chosen genome).\n- Validates the sites BED is headerless and that all rows have `start \u003C end`.\n- Calls `create_report` with `--flanking 300 --standalone`.\n- Writes a logs\u002F entry capturing the full command, the flanking value, the\n  per-region embedded data sizes, and the resolved track list — required\n  by the project's analysis-script audit-trail expectations.\n\nFor multi-sample cohorts, use `--samplesheet samplesheet.tsv` instead of\n`--bam\u002F--vcf`. Samplesheet format: `sample, bam_tumor, bam_normal, vcf, sites_bed`.\nThe driver emits one HTML per sample plus a top-level `index.html` that lists\nall samples with links. Pass `--jobs N` to build the per-sample HTMLs in\nparallel via `ThreadPoolExecutor` (each `create_report` call is I\u002FO-bound on\nBAM slicing, so threading scales well; `--jobs 6` for a 6-patient cohort\nroughly 1\u002FNx wall-clock vs sequential). Default is `--jobs 1`. Layout matches\nthe ATLL viral-integration reference implementation:\n\n```\nresults\u002F\u003Crun>\u002F\n├── inputs\u002F\u003Csample>\u002Fsites.\u003Cgenome>.bed\n├── reports\u002F\u003Csample>.\u003Cgenome>.html\n├── reports\u002Findex.html\n└── logs\u002Frun_\u003Ctimestamp>.log\n```\n\n## prep-track — fixing a non-bgzip track\n\nIf a GFF3\u002FGTF\u002FBED.gz is plain-gzip rather than bgzip, igv-reports fails\nsilently or with an obscure error. Two modes:\n\n**In-place** (with `.bak.original_gzip` backup) — replaces the original:\n\n```bash\nbash scripts\u002Fprep_track.sh \u002Fpath\u002Fto\u002Ftrack.gff3.gz\n```\n\n**Sibling file** (non-destructive — original untouched) — write the\nbgzipped+indexed track to a new path. Use this when other pipelines point\nat the original `.gff3.gz` and you can't risk a brief window where the\nfile is replaced:\n\n```bash\nbash scripts\u002Fprep_track.sh \u002Fpath\u002Fto\u002Ftrack.gff3.gz \\\n    --out \u002Fpath\u002Fto\u002Ftrack.bgz.gff3.gz\n```\n\nThe script (both modes):\n1. Backs up the original to `\u003Cname>.bak.original_gzip` (in-place mode only).\n2. `gunzip -c`s the file.\n3. Sorts by `chr` then numeric `pos` (`sort -k1,1 -k4,4n`).\n   (Gencode delivers records interleaved by feature type at the same locus —\n   tabix requires pos-sorted.)\n4. `bgzip`s to target.\n5. `tabix -p \u003Cgff|gtf|bed>`s.\n6. Verifies a sample tabix query returns rows.\n\nRun from any env that has htslib's `bgzip` + `tabix` on PATH.\n\n**Diagnostic** — `file \u003Cname>` for distinguishing the two formats:\n- Plain gzip: `gzip compressed data, from Unix, original size \u003CN>`\n- bgzip:      `Blocked GNU Zip Format (BGZF; gzipped file with extra field)`\n\nThe `extra field` keyword is the bgzip giveaway.\n\n## When generating an answer.md \u002F run.sh for the user\n\nThe driver script (`build_igvreports.py`) deliberately abstracts the\nunderlying `create_report` flags — it sets `--standalone`, `--fasta`, the\n`--flanking 300` default, and the YAML-resolved annotation tracks\ninternally so the user doesn't have to remember them. That abstraction is\ngood for ergonomics but bad for auditability: a reviewer reading the\n`answer.md` later can't see what flags are actually being invoked without\nopening the driver source.\n\nTo keep both: when you produce a runnable command for the user, **also\ninclude a code block titled \"Equivalent direct create_report invocation\"\nthat shows the fully-expanded command** with all flags and resolved track\npaths inline. The user should see the wrapper command they're going to\nrun AND the underlying command it expands to. Example:\n\n````\n## Run\n\n```bash\npython build_igvreports.py --genome mm10 --sites peaks.mm10.bed \\\\\n    --bam .\u002Fdata\u002Fip.bam .\u002Fdata\u002Finput.bam \\\\\n    --output reports\u002Fpeaks_qc.mm10.html\n```\n\n### Equivalent direct create_report invocation\n\n```bash\ncreate_report peaks.mm10.bed \\\\\n    --fasta \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fmm10.fa \\\\\n    --flanking 300 --standalone \\\\\n    --tracks .\u002Fdata\u002Fip.bam .\u002Fdata\u002Finput.bam \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fmm10_CpGIslands.bed \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fannotations\u002Fgencode.vM25.annotation.gtf.gz \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002FRepeatMaskerDB\u002F...\u002Frmsk_all_repeats_mm10.bed.gz \\\\\n    --title \"ChIP-seq peak QC (mm10) — IP vs Input\" \\\\\n    --output reports\u002Fpeaks_qc.mm10.html\n```\n````\n\nThis costs you ~10 lines and gives the reviewer a full audit trail. For\ncohort runs, show the expanded form for ONE representative sample only —\nthe others differ only in BAM\u002FVCF paths.\n\n## Post-render verification\n\n`scripts\u002Fverify_report.py` parses a built HTML and confirms it actually\ncontains what its inputs declared. Six checks: `html_exists`,\n`html_min_size`, `region_count` (tableJson rows == sites BED rows),\n`region_coords` (each BED row finds a matching `(chrom, start+1, end[, name])`\nin tableJson — BED is 0-based, the HTML stores 1-based start), `region_sessions`\n(sessionDictionary has one entry per row), and `tracks_present` (every\n`name` from `--track-config` or every basename from positional `--tracks`\nappears in the decoded igv.js session's `tracks[].name` list).\n\n```bash\npython scripts\u002Fverify_report.py \\\n    --html         results\u002F\u003Crun>\u002Freports\u002Fsample.hg38.html \\\n    --sites        results\u002F\u003Crun>\u002Finputs\u002Fsites.hg38.bed \\\n    --track-config results\u002F\u003Crun>\u002Finputs\u002Ftracks.json \\\n    --min-size-mb  1.0 \\\n    --out          results\u002F\u003Crun>\u002Freports\u002Fsample.verify.tsv \\\n    --fail-on-fail\n```\n\nOutput is a TSV with columns `check \u002F status \u002F observed \u002F expected \u002F details`\n(also printed to stdout). With `--fail-on-fail`, exits nonzero if any check\nis FAIL — wire this into Snakemake \u002F CI so the pipeline gates on render\nquality, not just on `create_report`'s exit code.\n\nNOTE: `--standalone` replaces every track URL with an inlined `data:` URL\nafter slicing, so URL paths are unrecoverable from the embedded session.\nThe check matches on track NAMES (which `--standalone` preserves) — for\n`--track-config` JSON pass meaningful names; positional `--tracks` mode\nuses basenames.\n\n### Cohort-level verification (`verify_cohort.py`)\n\nThe per-sample verifier above confirms each HTML is internally consistent\nbut cannot tell whether sample-1's HTML accidentally embeds sample-2's BAM\n(e.g., samplesheet typo, copy-paste, tumor\u002Fnormal slot swap). For cohort\nruns, `scripts\u002Fverify_cohort.py` adds five cross-sample checks:\n\n| Check | What it asserts |\n|---|---|\n| `cohort_html_coverage` (global) | Each samplesheet row has exactly one HTML; flags missing + extras |\n| `sample_tracks_match` (per-sample) | Each HTML's session contains every BAM\u002FVCF basename declared in THAT row |\n| `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) |\n| `sample_id_embedded` (per-sample) | The `sample` column value appears in the HTML's `\u003Ctitle>` or filename |\n| `index_consistency` (global) | `index.html` links exactly the samplesheet sample set; each target exists and is non-empty |\n\n**Auto-invoked by default** at the end of `build_igvreports.py --samplesheet`\ncohort runs. Disable with `--no-verify`; gate the pipeline with\n`--fail-on-fail`. Standalone invocation:\n\n```bash\npython scripts\u002Fverify_cohort.py \\\n    --samplesheet samplesheet.tsv \\\n    --reports-dir results\u002F\u003Crun>\u002Freports\u002F \\\n    --genome hg38 \\\n    --out results\u002F\u003Crun>\u002Freports\u002Fcohort_verify.tsv \\\n    --summary results\u002F\u003Crun>\u002Freports\u002Fcohort_verify.summary.md \\\n    --fail-on-fail\n```\n\nThe TSV adds a `sample` column on top of the per-sample verify schema, with\n`\"*\"` for cohort-global rows. The markdown rollup (`--summary`) groups\nPASS\u002FFAIL counts by check + lists every failure inline.\n\nWorked regression: `tests\u002Fintegration\u002Fcohort_verify\u002Fscenarios.sh` builds a\n3-sample cohort and asserts each of four corruption scenarios (missing\nHTML, sample swap, index drift, truncated HTML) triggers the expected\ncheck FAILs.\n\n### Content verification (`verify_anchors.py`) — opt-in, slow\n\n`verify_cohort.py` proves the HTML *says* the right thing. It can NOT\nconfirm the embedded BAM *slice* contains the data it claims to. Two\nfailure modes slip past structural checks:\n\n1. **Sample swap with matching basename** — the cohort loop wired the wrong\n   BAM into `sample_1`'s build, but the swapped BAM's `Path.stem` happens\n   to match what `sample_1`'s row declared (or two files in different dirs\n   share a basename). Track name passes; slice content is wrong.\n2. **Silent empty slice** — region rendered, but the slice has 0 reads\n   (failed `samtools index`, source BAM corruption, coords outside coverage).\n\n`scripts\u002Fverify_anchors.py` closes the gap by re-running `samtools view -c`\nagainst both the source BAM (at generate time) and the embedded slice (at\nverify time), then comparing counts. Two-mode workflow:\n\n```bash\n# 1. After the cohort renders cleanly, freeze the read counts as a regression fixture.\npython scripts\u002Fverify_anchors.py generate \\\n    --samplesheet samplesheet.tsv \\\n    --sites sites.hg38.bed \\\n    --out anchors.hg38.tsv\n\n# 2. Re-verify any time after — works against a fresh build of the same inputs,\n#    or to audit an existing HTML for unexpected content drift.\npython scripts\u002Fverify_anchors.py verify-cohort \\\n    --samplesheet samplesheet.tsv \\\n    --reports-dir results\u002F\u003Crun>\u002Freports\u002F \\\n    --genome hg38 \\\n    --anchors anchors.hg38.tsv \\\n    --out results\u002F\u003Crun>\u002Freports\u002Fcohort_verify_anchors.tsv \\\n    --fail-on-fail\n```\n\nOr chained into the build driver:\n\n```bash\n# Freeze anchors at build time:\npython scripts\u002Fbuild_igvreports.py --samplesheet ... --anchors-mode generate \\\n    --anchors anchors.hg38.tsv\n\n# Verify a later build against frozen anchors:\npython scripts\u002Fbuild_igvreports.py --samplesheet ... --anchors-mode verify \\\n    --anchors anchors.hg38.tsv --fail-on-fail\n```\n\nAnchors TSV schema (`#`-prefixed header per lab BED convention):\n\n```\n#sample\ttrack_name\ttrack_type\tchrom\tstart\tend\texpected\ttolerance\tmin\tmax\tnotes\n```\n\n`track_type` is one of:\n- `bam` — `expected` is the count from `samtools view -c -F 1536` against\n  the source BAM at generate time, and the same count against the\n  embedded BAM slice at verify time. Default when the column is absent\n  (backwards compat — pre-2026-05-19 anchor files keep working).\n- `bedgraph` — `expected` is the number of data rows in the source\n  bedGraph overlapping the region (CpG count for methylation data,\n  peak count for ChIP coverage). Verify-time count comes from the\n  wig\u002FbedGraph slice embedded by igv-reports in the HTML — gzip-decoded\n  in-memory, no samtools needed.\n\nbedGraph tracks come from the samplesheet's `extra_tracks` column.\nAnchors for them are generated automatically alongside BAM anchors when\nyou run `verify_anchors.py generate` against a samplesheet that includes\nbedGraph entries (e.g. `*.5mC.bedgraph`, `*.5hmC.bg`, plain or `.gz`).\n\n`tolerance` is a ratio (default 5%). `min`\u002F`max` are absolute bounds that\noverride tolerance when set — useful for known-positive sites like\n\"this integration must have ≥20 reads\" or \"this promoter must have ≥10 CpGs\".\n\nsamtools is resolved in this order: `--samtools-sif PATH` → `$SAMTOOLS_SIF`\n→ `\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fcontainers\u002Fsamtools_v1.23.1.sif` →\nPATH `samtools`. SIF preferred per `rules\u002Fapptainer_vs_conda.md`.\nbedGraph anchors don't require samtools.\n\n**Why this matters for methylation viewers**: the silent-failure mode for\nmethylation reports is \"region rendered, slice has 0 CpGs\" — an empty\nbedGraph slice because the source had no calls in that window, or\nbecause the slice extraction silently dropped them. Pure structural\nverification (`verify_cohort.py`) confirms the bedGraph track is in the\nHTML but can't tell whether it's empty. The bedgraph-anchor mode closes\nthis gap.\n\n**Why opt-in and not default:** the verify step shells out to samtools per\n(sample × region) and indexes each slice — ~1 s\u002Fanchor. For a 6-sample\ncohort × 50 regions that's ~5 min on top of the structural verify (which\nruns in seconds). Reach for this when sample swap or content regression\nis a real concern; the structural verifier is sufficient for routine builds.\n\nWorked regression: `tests\u002Fintegration\u002Fanchor_verify\u002Fscenarios.sh` builds a\n2-sample cohort and asserts each of four content scenarios (tolerance\nviolation, min-bound violation, corrupted slice, missing anchor) triggers\nthe expected PASS \u002F FAIL \u002F SKIP outcome.\n\n### Triage interpretation (`interpret_reports.py`) — human-first reading surface\n\nAfter `verify-cohort` produces a checks TSV, `interpret_reports.py` rolls it\nup into a single cohort-wide `interpretation.md`: a summary table (sample ×\nverdict counts) then per-sample sections with a per-region verdict.\n\n```bash\npython scripts\u002Finterpret_reports.py \\\n    --checks results\u002F\u003Crun>\u002Freports\u002Fcohort_verify_anchors.tsv \\\n    --out    results\u002F\u003Crun>\u002Freports\u002Finterpretation.md\n```\n\nPer-region verdict, derived **solely** from the anchor results (no new\nthresholds):\n\n- `PASS` — every non-SKIP anchor for the region passed\n- `FAIL` — every non-SKIP anchor failed\n- `REVIEW` — mixed pass\u002Ffail across the region's tracks\n- `UNVERIFIED` — every anchor was SKIP (region not rendered, or no tracks\n  matched) — distinct from FAIL: nothing was checked\n\nRegions sort FAIL-first (FAIL → REVIEW → UNVERIFIED → PASS) so the ones\nneeding attention sit on top of each sample section; FAIL\u002FREVIEW\u002FUNVERIFIED\nget a per-track breakdown (observed vs expected + the reason), PASS collapses\nto one line. If the checks TSV is missing or empty, a fallback file points\nthe reader at the HTML instead. It's a reporting tool — it always exits 0 on a\nsuccessful write; `verify_anchors --fail-on-fail` remains the CI gate.\n\nWorked regression: `tests\u002Fintegration\u002Finterpret\u002Fscenarios.sh` renders a\nsynthetic cohort and asserts the summary totals, FAIL-first ordering, the\nmalformed-input exit-2 path, and the missing-input fallback.\n\n## Output and workflow logging\n\nEvery run logs to `logs\u002Frun_\u003CYYYYMMDD_HHMMSS>.log` next to the reports dir.\nThe log captures:\n- Resolved track paths (per genome, after databases_config.yaml lookup).\n- The exact `create_report` command.\n- The flanking value used (default **300 bp** — this is the value that's\n  baked into all the embedded data slices, so audit trails depend on it).\n- Per-region embedded data sizes (extracted post-render so the user can\n  see which regions inflated the HTML).\n- Total HTML size.\n\nThis satisfies CLAUDE.md §\"Logging and Audit Trail\" — every run is\nreproducible from the log alone.\n\n## Track choice nuances\n\nFor gencode on hg38, the default points at\n`gencode.v47.annotation.gff3.gz` (full annotation, bgzip + tabix). This\ngives transcript models with exons \u002F CDS \u002F UTRs. The gene-level-only\ncompanion (`gencode.v47.genes.annotation.sorted.gff3.gz`) renders only\nsolid gene boxes and is fine for high-zoom views, but the full annotation\nis the right default for read-level inspection at integration \u002F fusion \u002F\nSV junctions.\n\nFor mouse genomes, `databases_config.yaml` ships `.gtf.gz` paths instead.\nGTFs work in igv-reports if bgzip + tabix-indexed; **prep-track** converts\nplain-gzip GTFs the same way it does GFF3s.\n\nFor T2T-CHM13, only the FASTA + GTF + CGI are indexed in our DB; rmsk is\nabsent and is auto-skipped by the driver. The variant table will load\nwithout rmsk; flag this in the run log.\n\n## Common-case examples\n\nThe `examples\u002F` directory has runnable templates:\n\n- `single_sample.sh` — one BAM + one VCF + a sites BED → one HTML.\n- `cohort_samplesheet.sh` — TSV-driven multi-sample run.\n- `prep_track_demo.sh` — convert a plain-gzip gencode to bgzip+tabix.\n- `methylation_ont\u002F` — ONT 5mC\u002F5hmC viewer (BAM with `colorBy: basemod2`\n  + per-sample bedGraph at fixed y-axis 0..100). End-to-end worked\n  example with pre-sliced data; recipe.md explains the slots.\n\nThese are reference implementations; copy and edit them for new runs\nrather than starting from scratch.\n\n## Tests\n\nThree-layer suite under `tests\u002F`, orchestrated by `tests\u002Frun_all.sh`:\n\n| Layer | What it covers | Runtime | Needs |\n|---|---|---|---|\n| **unit** (`tests\u002Funit\u002F`) | 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 |\n| **smoke** (`tests\u002Fsmoke\u002F`) | `samtools_count` \u002F `samtools_index` \u002F full slice-decode-and-count round-trip against the committed `tests\u002Ffixtures\u002Ftiny_colo829.hg38.bam` (457 KB, sliced from public ONT COLO829 release) | ~3 s | pytest + samtools (SIF or PATH) |\n| **integration** (`tests\u002Fintegration\u002F`) | end-to-end: build a 2-\u002F3-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 |\n\n```bash\nbash tests\u002Frun_all.sh                  # all three layers\nbash tests\u002Frun_all.sh --unit-only      # ~1 s — fastest feedback loop\nbash tests\u002Frun_all.sh --no-integration # ~12 s — works on any machine\nbash tests\u002Frun_all.sh --integration-only\n```\n\nThe fixture provenance + regeneration recipe live in\n[tests\u002Ffixtures\u002FREADME.md](tests\u002Ffixtures\u002FREADME.md). Anchor counts the\nsmoke layer expects (chr2=5, chr7=9) are the contract — any fixture\nregeneration that changes them must also update the smoke test constants.\n\n## ONT methylation viewers (specialized path)\n\nFor per-read 5mC\u002F5hmC visualization the positional `--tracks` API does\nnot work — you need named tracks with `colorBy: \"basemod2\"` on the BAMs\nand `min: 0, max: 100` on the bedGraph tracks (cross-sample y-axis lock,\nsee `rules\u002Figv.md`). Use the `--track-config \u003Cjson>` passthrough:\n\n```bash\n# 1. Write a YAML spec listing samples (see tracks_spec.example.yaml).\n# 2. Generate tracks.json with the right defaults baked in:\npython scripts\u002Fgenerate_tracks_json.py \\\n    --spec tracks_spec.yaml --run-dir results\u002F\u003Crun>\u002F \\\n    --out results\u002F\u003Crun>\u002Ftracks.json\n\n# 3. Build the report:\npython scripts\u002Fbuild_igvreports.py \\\n    --sites results\u002F\u003Crun>\u002Fsites.hg38.bed \\\n    --track-config results\u002F\u003Crun>\u002Ftracks.json \\\n    --genome hg38 --flanking 0 \\\n    --type mutation --info-columns name \\\n    --output results\u002F\u003Crun>\u002Fmethylation_report.hg38.html\n```\n\n### Annotation shortcuts in the YAML\n\nThe default `--tracks` path (SV\u002Fvariant viewers) auto-resolves CpG islands,\ngencode, and RepeatMasker from `databases_config.yaml` when you pass\n`--genome hg38`. On the `--track-config` (methylation) path you used to\nhave to hand-paste those paths into the YAML. As of the methylation-polish\nround, you can use a `default:` shortcut for the same resolution:\n\n```yaml\ngenome: hg38\n\nannotation:\n  # SHORTCUT — resolved from databases_config.yaml for the genome above.\n  # Gets an Okabe-Ito color + sensible displayMode you can override per entry.\n  - default: gencode\n  - default: cgi\n  - default: repmasker\n  - default: epdnew_coding         # hg38 only\n  - default: epdnew_noncoding      # hg38 only\n\n  # Mix with EXPLICIT entries when needed (e.g. a pre-sliced custom track):\n  - name: \"My custom peak set\"\n    url: peaks\u002Fpromoter_slices.bed\n    format: bed\n```\n\nValid `default:` keys: `cgi`, `gencode`, `repmasker`, `epdnew_coding`,\n`epdnew_noncoding`. Mixing both forms is supported; order is preserved.\nOverride the canned `name`\u002F`color`\u002F`displayMode` per entry by adding the\nfield alongside `default:`. The shortcut needs a top-level `genome:` in\nthe spec and reads `databases_config.yaml` (resolution path same as the\nSV viewer driver — points at `$IGV_REPORTS_DB_CONFIG` if set, else the\nlab default; override per-run with `--db-config PATH`).\n\nKey methylation-specific defaults:\n- `--flanking 0` (sites BED already encodes the window — promoter\u002Fgene span).\n- `--info-columns name` (surface the BED `name` column in the variant table).\n- `--type mutation` (one-locus view per row; not split-screen).\n- bedGraph not bigwig — igv-reports cannot slice `.bw` directly.\n\nWhen `--track-config` is set the driver bypasses the auto-resolved\ndefault annotation tracks (CGI \u002F gencode \u002F rmsk) and the `--bam` \u002F\n`--vcf` \u002F `--extra-track` flags — the JSON is the source of truth.\nBuild annotation slices into the JSON instead.\n\n**`--apptainer` is auto-detected**: the driver flips to the dedicated\nigv-reports 1.16.0 SIF (`\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fcontainers\u002Figv-reports_1.16.0.sif`,\n83 MB, pulled from Galaxy depot) when `SLURM_JOB_ID` is in the\nenvironment — i.e. running on a compute node where the NFS conda\ncold-start tax matters (`rules\u002Fapptainer_vs_conda.md`). On the login\nnode it stays on the conda env. Override with `--apptainer` \u002F\n`--no-apptainer`; the decision lands in the run log.\n\nFull recipe and rationale: `references\u002Fmethylation_ont.md`. Worked\nexample with real data: `examples\u002Fmethylation_ont\u002F`.\n\n## Exporting HTML and PNG side-by-side (`--also-png`)\n\nThe HTML report is the deep-dive view; sometimes you also need static\nPNGs you can email, drop in a Slack channel, or paste into slides. The\ndriver's `--also-png` flag invokes the sister `igver` skill against the\n**same sites BED and same track list** that drove `create_report`, so\nboth artifacts cover identical regions with matching content.\n\n```bash\npython scripts\u002Fbuild_igvreports.py \\\n    --samplesheet samplesheet.tsv \\\n    --genome hg38 \\\n    --output-dir results\u002Frun\u002Freports\u002F \\\n    --jobs 6 \\\n    --also-png \\\n    --png-dpi 600 --png-display-mode collapse\n```\n\nOutput layout per sample:\n\n```\nresults\u002Frun\u002Freports\u002F\n├── \u003Csample>.hg38.html              # interactive\n├── png_\u003Csample>.hg38\u002F\n│   ├── igver_regions.bed           # flanked BED with UIDs (igver -r)\n│   ├── igver_input.txt             # track paths, one per line (igver -i)\n│   ├── manifest.tsv                # bridge: BED row ↔ PNG ↔ HTML row\n│   └── png\u002F\n│       ├── chr1-100-500.alpha.png  # one PNG per region\n│       └── chr2-0-700.beta.png\n└── index.html\n```\n\n### How consistency is guaranteed — five levers\n\n1. **Single sites BED with `--flanking` baked in.** The driver writes\n   `igver_regions.bed` with `start − flanking` and `end + flanking`\n   already applied (clamped to 0 on the low side); igver sees the same\n   coordinates create_report's igv.js viewer slices to.\n2. **Single resolved track list.** On the default (positional) path the\n   exact `[BAMs, VCF, extras, defaults]` list passed to `create_report` is\n   also written to `igver_input.txt`. On the `--track-config` path the\n   local-path `url:` entries from the JSON are extracted (http(s) URLs are\n   skipped — igver can't consume them).\n3. **Matched display mode.** Default is `--png-display-mode collapse` to\n   line up with the HTML's `BAM_DEFAULTS displayMode: COLLAPSED`. Override\n   to `expand` for per-read SV inspection on both artifacts.\n4. **UID-based filenames.** The BED's `name` column (auto-assigned\n   `region_\u003Cidx>` when missing) becomes both the HTML table label (via\n   `--info-columns name`) and the PNG filename suffix\n   (`\u003Cchr-start-end>.\u003Cuid>.png`). A user finds the same region in either\n   artifact by the same string.\n5. **`manifest.tsv` audit trail.** Per-sample TSV with columns:\n   `bed_row_idx, uid, chrom, start_orig, end_orig, start_flanked,\n   end_flanked, region, png_path, html_path, html_table_row`. One row\n   per region in BED order. `verify_cohort.py` reads this to run three\n   PNG-side checks (count matches, exist + non-empty, html-row contiguity).\n\n### Resolution of the `igver` invocation\n\nOrder, first match wins:\n1. `--igver-cmd '...'` (split on whitespace — supports `apptainer exec ... igver`).\n2. `$IGVER_CMD` env var (same shape).\n3. `igver` on PATH.\n4. Lab apptainer SIF at `\u002Fdata1\u002Fgreenbab\u002Fsoftware\u002Fimages\u002Figver_latest.sif`\n   (only if reachable).\n\nIf none resolve, the build exits before invoking create_report so you\ndon't pay the HTML cost before finding out PNGs are unavailable.\n\n### Methylation caveat (bigwig vs bedGraph)\n\nThe HTML methylation path uses **bedGraph** tracks (igv.js consumes\nthose directly); igver's per-read methylation view uses **BAMs** with\n`--color-by BASE_MODIFICATION`, and igver's cross-sample comparison view\nuses **bigwig** tracks. Content can be made identical only if both\nformats trace back to the same `modkit pileup` output (`modkit bedmethyl\ntobigwig` of the same bedGraph). The driver's `--also-png` passes the\nJSON's `url:` entries through verbatim, so if your YAML lists bedGraphs\nthey'll go to igver as-is — igver will render them but the result may\nlook different from the HTML's color-coded per-read view. For\npublication-quality methylation PNGs, supply a parallel `tracks.json`\nthat lists bigwigs and run `igver` separately (see `rules\u002Figv.md`).\n\nFor SV\u002Fvariant viewers this caveat doesn't apply — both render the\nidentical BAMs and the result is content-equivalent.\n\n### Cross-artifact verification\n\nThe driver runs an **inline existence check** right after igver returns:\nwalks each expected PNG path (`\u003Cchr>-\u003Cstart>-\u003Cend>.\u003Cuid>.\u003Cext>` derived\nfrom the manifest) and fails the build with an actionable message if\nany are missing or zero-byte. This catches igver's documented\nsilent-exit-0 failure mode (egg-link install without the IGV Java\nbinary; see `rules\u002Figv.md`) — `proc.returncode != 0` alone misses it.\n\nIn addition, `verify_cohort.py` then runs three checks per sample:\n\n| Check | Catches |\n|---|---|\n| `png_count_matches_bed` | partial igver run (SIGKILL mid-batch), stale manifest from a previous build, filename collisions |\n| `pngs_exist_and_nonempty` | empty IGV screenshots (\u003C 10 KB threshold; useful screenshots are typically ≥ 50 KB) |\n| `png_html_row_alignment` | manifest rows referencing a different HTML, html_table_row not contiguous 1..N |\n\n`--png-min-size-kb 5.0` lowers the threshold if you have legitimate\nno-data regions where igver produces a near-empty PNG.\n\n## Credits\n\nThe HTML rendering engine here is **not** this skill — it is the upstream\n[`igv-reports`](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) package\n(`create_report`) maintained by the IGV team at the Broad Institute (MIT,\n© 2018-2019 The Broad Institute and The Regents of the University of\nCalifornia). The rendered HTML embeds `igv.js` from the same team. This\nskill is a driver + verifier layer on top.\n\nWhen producing outputs that will end up in a publication, cite the igv.js\npaper: Robinson, J. T., Thorvaldsdóttir, H., Turner, D., & Mesirov, J. P.\n(2023). *igv.js: an embeddable JavaScript implementation of the\nIntegrative Genomics Viewer (IGV).* Bioinformatics, 39(1), btac830.\n[doi:10.1093\u002Fbioinformatics\u002Fbtac830](https:\u002F\u002Fdoi.org\u002F10.1093\u002Fbioinformatics\u002Fbtac830).\n\nFull attribution, the upstream MIT license verbatim, the reuse-vs-add\ntable, and BibTeX entries live in [`CREDITS.md`](CREDITS.md).\n\n## See also\n\n- `references\u002Fbest_practices.md` — full create_report flag reference,\n  format gotchas, performance notes. Read this if a run fails in a way\n  not listed in the Pitfalls table above.\n- `references\u002Fdatabases_config_paths.md` — per-genome track availability\n  matrix and exact YAML keys. Read this when adding a new genome or\n  diagnosing a missing-track warning.\n- `references\u002Fmethylation_ont.md` — ONT 5mC\u002F5hmC cheat-sheet (colorBy,\n  min:0\u002Fmax:100, flanking=0, bedGraph vs bigwig, EPDnew lookup).\n- `scripts\u002Fbuild_igvreports.py` — the driver. Reads `--samplesheet` or\n  `--bam\u002F--vcf` direct-args, resolves tracks, validates the sites BED,\n  writes the HTMLs and the run log. Supports `--track-config \u003Cjson>`\n  passthrough for fully-styled track sets.\n- `scripts\u002Fgenerate_tracks_json.py` — YAML spec → tracks.json with\n  ONT-methylation defaults baked in (colorBy=basemod2, min:0\u002Fmax:100,\n  group-paired Okabe-Ito colors).\n- `scripts\u002Fverify_report.py` — post-render structural verifier; parses\n  the HTML's embedded tableJson + sessionDictionary, confirms region\n  count \u002F coordinates \u002F track names match the inputs. Emits a verify.tsv\n  and gates on `--fail-on-fail`.\n- `scripts\u002Fverify_cohort.py` — cohort-level verifier; layered on top of\n  verify_report's per-sample checks, adds cross-sample contamination\n  scanning + index.html \u002F sample-id consistency. Auto-invoked at the end\n  of `build_igvreports.py --samplesheet`; standalone-runnable too.\n- `scripts\u002Fverify_anchors.py` — content verifier; samtools-counts the\n  embedded BAM slices and compares to anchors frozen from the source BAMs\n  at build time. Catches sample swaps that share basenames and silent\n  empty slices. Opt-in via `--anchors-mode generate|verify` on the build\n  driver; slow (~1 s\u002Fanchor). See SKILL.md content-verification section.\n- `scripts\u002Fprep_track.sh` — gunzip → sort → bgzip → tabix utility.\n- `igv-screenshots` skill — the **static PNG\u002FPDF\u002FSVG** counterpart based\n  on igver. Use it instead of this one when the deliverable is a\n  publication-quality figure rather than a clickable viewer.\n- Reference implementation:\n  `\u002Fdata1\u002Fgreenbab\u002Fprojects\u002Font\u002FProject_17424\u002Fresults\u002F20260503_hg38plusHTLV1EBV_cohort_integration_igvreports\u002F`\n  — 6-patient ATLL cohort viral-integration HTMLs + DNMT3A sanity check;\n  this skill was extracted from that work.\n","# igv-reports\n\nThis skill builds **self-contained HTML genomic-region reports** with\n[igv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) (`create_report`).\nEach report is a single browseable HTML containing the igv.js viewer plus\nembedded data slices for every region. No server, no internet, no IGV\ninstall needed at view time.\n\nThe skill has three entry points:\n- **build** — one-shot: sites BED + BAM(s) ± VCF → HTML.\n- **cohort** — multi-sample driver from a samplesheet → per-sample HTMLs + index.\n- **prep-track** — utility: convert plain-gzip GFF\u002FGTF\u002FBED.gz into a\n  bgzip + tabix-indexed track that igv-reports can load.\n\n## What this skill is (and is not)\n\nThis skill is a **driver layer** on top of the upstream `igv-reports`\nPython package by the IGV team\n([github.com\u002Figvteam\u002Figv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports)).\nThe naming is unavoidable — both share the `igv-reports` name.\n\n| Component | Source | Role |\n|---|---|---|\n| `create_report` CLI | upstream PyPI package `igv-reports` | does the actual HTML rendering |\n| `scripts\u002Fbuild_igvreports.py` | **this skill** | wraps `create_report` with default-track resolution, cohort\u002Fsamplesheet mode, SIF auto-detect |\n| `scripts\u002Fverify_{report,cohort,anchors}.py` | **this skill** | post-render structural + content audits (not in upstream) |\n| `scripts\u002Fprep_track.sh` | **this skill** | bgzip+tabix utility for annotation tracks |\n\nThe skill is not on PyPI — it's a directory of scripts. Use it by either\ncloning this repo or copying `scripts\u002F` next to your data.\n\n## Off-MSKCC quickstart\n\nDefaults assume MSKCC HPC (lab SIF, `databases_config.yaml`, `\u002Fdata1\u002Fgreenbab`\nbind). Anywhere else:\n\n```bash\n# 1. Install the UPSTREAM igv-reports package (provides `create_report`).\n#    Use -U to pick up the latest fixes; skill requires >=1.16.0.\npip install -U 'igv-reports>=1.16.0'\n\n# 2. Get this skill's wrapper scripts (one of):\n#    - clone:  git clone https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill.git\n#              cd igv-reports-skill\n#    - or copy scripts\u002F next to your project\n\n# 3. Run the wrapper, bypassing the lab databases YAML with explicit paths:\npython scripts\u002Fbuild_igvreports.py \\\n    --genome hg38 \\\n    --sites sites.hg38.bed \\\n    --bam tumor.bam normal.bam \\\n    --fasta \u002Fpath\u002Fto\u002Fhg38.fa \\\n    --no-default-tracks \\\n    --extra-track \u002Fpath\u002Fto\u002Fyour_cpg_islands.bed.gz \\\n    --extra-track \u002Fpath\u002Fto\u002Fgencode.v47.annotation.gff3.gz \\\n    --output report.hg38.html\n```\n\nIf you only need raw `create_report` (no cohort mode, no verifiers, no\nauto-tracks), skip the skill entirely and use upstream directly —\nsee [igvteam\u002Figv-reports](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) docs.\n\nEnvironment overrides (all optional):\n\n| Var | Effect |\n|---|---|\n| `IGV_REPORTS_DB_CONFIG` | Path to your own databases YAML (same schema as the lab's) |\n| `IGV_REPORTS_SIF` | Path to your own `igv-reports` apptainer SIF |\n| `SAMTOOLS_SIF_DEFAULT` | Path to your own `samtools` SIF (verifier only) |\n| `IGV_REPORTS_BIND` | Colon-separated bind paths for singularity (default `\u002Fdata1\u002Fgreenbab`). Empty string disables binding. |\n\nDriver flags `--fasta` and `--no-default-tracks` let you skip the databases\nYAML entirely without setting any env var. `--no-apptainer` forces the\nPATH `create_report` path even on a SLURM node. The hermetic `tests\u002Funit\u002F`\nsuite runs anywhere with `pytest` + Python ≥ 3.10.\n\n## When to use which entry point\n\n| User request | Entry point |\n|---|---|\n| \"Make an HTML for these 5 SV breakpoints in tumor.bam\" | **build** |\n| \"Give me one HTML per patient for the cohort integration calls\" | **cohort** |\n| \"create_report fails with 'not BGZF' on this gencode\" | **prep-track** |\n\n## Defaults (locked in)\n\n- Tracks always loaded, top-to-bottom in the viewer:\n  1. CpG islands (BED, plain or bgzipped)\n  2. Gencode full annotation (GFF3.gz, **transcripts + exons + CDS + UTRs**, NOT a gene-level-only file)\n  3. RepeatMasker (BED.gz, bgzipped + tabix-indexed)\n  Plus the user's BAM(s), VCF, and any extra tracks they pass.\n- `--flanking 300` bp on either side of each site (good for SV breakpoints\n  and point variants alike). Override per call if needed.\n- `--standalone` so the HTML is offline-viewable.\n- Output filename includes the genome tag — e.g. `cohort.hg38.html` —\n  to pass `enforce-genome-tag.sh`.\n- Reference FASTA is resolved from `databases_config.yaml`:\n  `\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fllm_configs\u002Fclaude\u002Fprofiles\u002Fdatabases\u002Fdatabases_config.yaml`\n  (lab default; override with `$IGV_REPORTS_DB_CONFIG` or `--no-default-tracks` + `--fasta`).\n  Supported genome IDs: `hg38`, `mm10`, `mm39`, `t2t_CHM13v2_plusY`, `GRCh37`.\n- Per-genome default track availability is recorded in\n  `references\u002Fdatabases_config_paths.md` — read it before assembling tracks\n  so the skill doesn't try to load a track that doesn't exist for the\n  selected genome (e.g., mm39 has no rmsk in our database).\n\n## Sites BED format (critical)\n\nigv-reports' BED parser reads fields **by position** and trips on a header\nrow (`ValueError: invalid literal for int() with base 10: 'start'`). Always\nemit a **plain headerless 4-column BED**:\n\n```\nchr    start    end    name\nchr2   25227855 25342590 DNMT3A_full_gene\n```\n\nTab-separated. The `name` becomes the row label in the report's variant\ntable — make it specific enough to identify the site after deduping.\n\nBy default `create_report` shows only the chr\u002Fstart\u002Fend position columns\nin the clickable table. To surface the `name` (or any extra columns from\na 5+ column BED), pass `--info-columns \u003Ccolname>` to the driver:\n\n```bash\npython scripts\u002Fbuild_igvreports.py ... --info-columns name\npython scripts\u002Fbuild_igvreports.py ... --info-columns gene_name,score\n```\n\nColumn names are matched by header (so a `#chrom\\tstart\\tend\\tname\\tscore`\nheader works). For positional BED without a header, the convention is the\n4th column = `name`, 5th = `score`, 6th = `strand`.\n\nThe project's `enforce-genome-tag.sh` hook requires a genome tag in the BED\nfilename: use `sites.hg38.bed`, not `sites.bed`.\n\n### `--type` for BED-style sites\n\nWhen the sites input is a BED (not a VCF), pass `--type mutation` to\n`create_report` (or the driver). This gives the right viewer behavior at\neach row — one locus per row, no split-screen, table on top. Without it,\nsome BED layouts trigger create_report's split-screen junction view by\nheuristic. Use `--type variant` for VCF sites, or omit for create_report's\nauto-detection (only safe with a VCF).\n\n```bash\npython scripts\u002Fbuild_igvreports.py ... --type mutation --info-columns name\n```\n\n## Pitfalls (the skill should encode and\u002For detect these)\n\n| Symptom | Root cause | Fix |\n|---|---|---|\n| `ValueError: invalid literal for int()` on first row | Header row in sites BED | Strip header — plain BED |\n| `UnicodeDecodeError: byte 0x8b` reading a track | igv-reports reading bgzip as text | Filename must end `.gff3.gz` \u002F `.bed.gz` AND be true bgzip (check with `file \u003Cname>` for \"extra field\") |\n| `tabix: not BGZF` | Track was plain-gzipped, not bgzipped | Run **prep-track** entry point |\n| `tabix: out of order` while indexing | GFF\u002FGTF\u002FBED records not pos-sorted within chr | **prep-track** does `sort -k1,1 -k4,4n` before bgzip |\n| 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` |\n| Genome ID lookup fails with `--genome hg38` | igv.js bundled IDs require internet at view + render time. Use `--fasta \u002Fpath\u002Fto\u002Flocal.fa` instead (always works offline) |\n\nFull pitfalls + create_report flag reference in `references\u002Fbest_practices.md`.\n\n## How to run — quick recipe\n\nActivate the snakemake conda env first; `create_report` lives there:\n\n```bash\nsource \u002Fhome\u002Fahunos\u002Fminiforge3\u002Fetc\u002Fprofile.d\u002Fconda.sh\nconda activate snakemake\n```\n\nThen call the bundled driver script (use `scripts\u002F` relative to the repo\nroot, or an absolute path to wherever the skill is checked out):\n\n```bash\npython scripts\u002Fbuild_igvreports.py \\\n    --sites results\u002Frun\u002Finputs\u002Fsites.hg38.bed \\\n    --bam tumor.bam normal.bam \\\n    --vcf calls.vcf \\\n    --genome hg38 \\\n    --output results\u002Frun\u002Freports\u002Fcohort.hg38.html\n```\n\nThe driver:\n- Resolves the genome's CpG \u002F gencode \u002F rmsk paths from `databases_config.yaml`\n  (skipping any that don't exist for the chosen genome).\n- Validates the sites BED is headerless and that all rows have `start \u003C end`.\n- Calls `create_report` with `--flanking 300 --standalone`.\n- Writes a logs\u002F entry capturing the full command, the flanking value, the\n  per-region embedded data sizes, and the resolved track list — required\n  by the project's analysis-script audit-trail expectations.\n\nFor multi-sample cohorts, use `--samplesheet samplesheet.tsv` instead of\n`--bam\u002F--vcf`. Samplesheet format: `sample, bam_tumor, bam_normal, vcf, sites_bed`.\nThe driver emits one HTML per sample plus a top-level `index.html` that lists\nall samples with links. Pass `--jobs N` to build the per-sample HTMLs in\nparallel via `ThreadPoolExecutor` (each `create_report` call is I\u002FO-bound on\nBAM slicing, so threading scales well; `--jobs 6` for a 6-patient cohort\nroughly 1\u002FNx wall-clock vs sequential). Default is `--jobs 1`. Layout matches\nthe ATLL viral-integration reference implementation:\n\n```\nresults\u002F\u003Crun>\u002F\n├── inputs\u002F\u003Csample>\u002Fsites.\u003Cgenome>.bed\n├── reports\u002F\u003Csample>.\u003Cgenome>.html\n├── reports\u002Findex.html\n└── logs\u002Frun_\u003Ctimestamp>.log\n```\n\n## prep-track — fixing a non-bgzip track\n\nIf a GFF3\u002FGTF\u002FBED.gz is plain-gzip rather than bgzip, igv-reports fails\nsilently or with an obscure error. Two modes:\n\n**In-place** (with `.bak.original_gzip` backup) — replaces the original:\n\n```bash\nbash scripts\u002Fprep_track.sh \u002Fpath\u002Fto\u002Ftrack.gff3.gz\n```\n\n**Sibling file** (non-destructive — original untouched) — write the\nbgzipped+indexed track to a new path. Use this when other pipelines point\nat the original `.gff3.gz` and you can't risk a brief window where the\nfile is replaced:\n\n```bash\nbash scripts\u002Fprep_track.sh \u002Fpath\u002Fto\u002Ftrack.gff3.gz \\\n    --out \u002Fpath\u002Fto\u002Ftrack.bgz.gff3.gz\n```\n\nThe script (both modes):\n1. Backs up the original to `\u003Cname>.bak.original_gzip` (in-place mode only).\n2. `gunzip -c`s the file.\n3. Sorts by `chr` then numeric `pos` (`sort -k1,1 -k4,4n`).\n   (Gencode delivers records interleaved by feature type at the same locus —\n   tabix requires pos-sorted.)\n4. `bgzip`s to target.\n5. `tabix -p \u003Cgff|gtf|bed>`s.\n6. Verifies a sample tabix query returns rows.\n\nRun from any env that has htslib's `bgzip` + `tabix` on PATH.\n\n**Diagnostic** — `file \u003Cname>` for distinguishing the two formats:\n- Plain gzip: `gzip compressed data, from Unix, original size \u003CN>`\n- bgzip:      `Blocked GNU Zip Format (BGZF; gzipped file with extra field)`\n\nThe `extra field` keyword is the bgzip giveaway.\n\n## When generating an answer.md \u002F run.sh for the user\n\nThe driver script (`build_igvreports.py`) deliberately abstracts the\nunderlying `create_report` flags — it sets `--standalone`, `--fasta`, the\n`--flanking 300` default, and the YAML-resolved annotation tracks\ninternally so the user doesn't have to remember them. That abstraction is\ngood for ergonomics but bad for auditability: a reviewer reading the\n`answer.md` later can't see what flags are actually being invoked without\nopening the driver source.\n\nTo keep both: when you produce a runnable command for the user, **also\ninclude a code block titled \"Equivalent direct create_report invocation\"\nthat shows the fully-expanded command** with all flags and resolved track\npaths inline. The user should see the wrapper command they're going to\nrun AND the underlying command it expands to. Example:\n\n````\n## Run\n\n```bash\npython build_igvreports.py --genome mm10 --sites peaks.mm10.bed \\\\\n    --bam .\u002Fdata\u002Fip.bam .\u002Fdata\u002Finput.bam \\\\\n    --output reports\u002Fpeaks_qc.mm10.html\n```\n\n### Equivalent direct create_report invocation\n\n```bash\ncreate_report peaks.mm10.bed \\\\\n    --fasta \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fmm10.fa \\\\\n    --flanking 300 --standalone \\\\\n    --tracks .\u002Fdata\u002Fip.bam .\u002Fdata\u002Finput.bam \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fmm10_CpGIslands.bed \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002Fmm10\u002Fannotations\u002Fgencode.vM25.annotation.gtf.gz \\\\\n        \u002Fdata1\u002Fgreenbab\u002Fdatabase\u002FRepeatMaskerDB\u002F...\u002Frmsk_all_repeats_mm10.bed.gz \\\\\n    --title \"ChIP-seq peak QC (mm10) — IP vs Input\" \\\\\n    --output reports\u002Fpeaks_qc.mm10.html\n```\n````\n\nThis costs you ~10 lines and gives the reviewer a full audit trail. For\ncohort runs, show the expanded form for ONE representative sample only —\nthe others differ only in BAM\u002FVCF paths.\n\n## Post-render verification\n\n`scripts\u002Fverify_report.py` parses a built HTML and confirms it actually\ncontains what its inputs declared. Six checks: `html_exists`,\n`html_min_size`, `region_count` (tableJson rows == sites BED rows),\n`region_coords` (each BED row finds a matching `(chrom, start+1, end[, name])`\nin tableJson — BED is 0-based, the HTML stores 1-based start), `region_sessions`\n(sessionDictionary has one entry per row), and `tracks_present` (every\n`name` from `--track-config` or every basename from positional `--tracks`\nappears in the decoded igv.js session's `tracks[].name` list).\n\n```bash\npython scripts\u002Fverify_report.py \\\n    --html         results\u002F\u003Crun>\u002Freports\u002Fsample.hg38.html \\\n    --sites        results\u002F\u003Crun>\u002Finputs\u002Fsites.hg38.bed \\\n    --track-config results\u002F\u003Crun>\u002Finputs\u002Ftracks.json \\\n    --min-size-mb  1.0 \\\n    --out          results\u002F\u003Crun>\u002Freports\u002Fsample.verify.tsv \\\n    --fail-on-fail\n```\n\nOutput is a TSV with columns `check \u002F status \u002F observed \u002F expected \u002F details`\n(also printed to stdout). With `--fail-on-fail`, exits nonzero if any check\nis FAIL — wire this into Snakemake \u002F CI so the pipeline gates on render\nquality, not just on `create_report`'s exit code.\n\nNOTE: `--standalone` replaces every track URL with an inlined `data:` URL\nafter slicing, so URL paths are unrecoverable from the embedded session.\nThe check matches on track NAMES (which `--standalone` preserves) — for\n`--track-config` JSON pass meaningful names; positional `--tracks` mode\nuses basenames.\n\n### Cohort-level verification (`verify_cohort.py`)\n\nThe per-sample verifier above confirms each HTML is internally consistent\nbut cannot tell whether sample-1's HTML accidentally embeds sample-2's BAM\n(e.g., samplesheet typo, copy-paste, tumor\u002Fnormal slot swap). For cohort\nruns, `scripts\u002Fverify_cohort.py` adds five cross-sample checks:\n\n| Check | What it asserts |\n|---|---|\n| `cohort_html_coverage` (global) | Each samplesheet row has exactly one HTML; flags missing + extras |\n| `sample_tracks_match` (per-sample) | Each HTML's session contains every BAM\u002FVCF basename declared in THAT row |\n| `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) |\n| `sample_id_embedded` (per-sample) | The `sample` column value appears in the HTML's `\u003Ctitle>` or filename |\n| `index_consistency` (global) | `index.html` links exactly the samplesheet sample set; each target exists and is non-empty |\n\n**Auto-invoked by default** at the end of `build_igvreports.py --samplesheet`\ncohort runs. Disable with `--no-verify`; gate the pipeline with\n`--fail-on-fail`. Standalone invocation:\n\n```bash\npython scripts\u002Fverify_cohort.py \\\n    --samplesheet samplesheet.tsv \\\n    --reports-dir results\u002F\u003Crun>\u002Freports\u002F \\\n    --genome hg38 \\\n    --out results\u002F\u003Crun>\u002Freports\u002Fcohort_verify.tsv \\\n    --summary results\u002F\u003Crun>\u002Freports\u002Fcohort_verify.summary.md \\\n    --fail-on-fail\n```\n\nThe TSV adds a `sample` column on top of the per-sample verify schema, with\n`\"*\"` for cohort-global rows. The markdown rollup (`--summary`) groups\nPASS\u002FFAIL counts by check + lists every failure inline.\n\nWorked regression: `tests\u002Fintegration\u002Fcohort_verify\u002Fscenarios.sh` builds a\n3-sample cohort and asserts each of four corruption scenarios (missing\nHTML, sample swap, index drift, truncated HTML) triggers the expected\ncheck FAILs.\n\n### Content verification (`verify_anchors.py`) — opt-in, slow\n\n`verify_cohort.py` proves the HTML *says* the right thing. It can NOT\nconfirm the embedded BAM *slice* contains the data it claims to. Two\nfailure modes slip past structural checks:\n\n1. **Sample swap with matching basename** — the cohort loop wired the wrong\n   BAM into `sample_1`'s build, but the swapped BAM's `Path.stem` happens\n   to match what `sample_1`'s row declared (or two files in different dirs\n   share a basename). Track name passes; slice content is wrong.\n2. **Silent empty slice** — region rendered, but the slice has 0 reads\n   (failed `samtools index`, source BAM corruption, coords outside coverage).\n\n`scripts\u002Fverify_anchors.py` closes the gap by re-running `samtools view -c`\nagainst both the source BAM (at generate time) and the embedded slice (at\nverify time), then comparing counts. Two-mode workflow:\n\n```bash\n# 1. After the cohort renders cleanly, freeze the read counts as a regression fixture.\npython scripts\u002Fverify_anchors.py generate \\\n    --samplesheet samplesheet.tsv \\\n    --sites sites.hg38.bed \\\n    --out anchors.hg38.tsv\n\n# 2. Re-verify any time after — works against a fresh build of the same inputs,\n#    or to audit an existing HTML for unexpected content drift.\npython scripts\u002Fverify_anchors.py verify-cohort \\\n    --samplesheet samplesheet.tsv \\\n    --reports-dir results\u002F\u003Crun>\u002Freports\u002F \\\n    --genome hg38 \\\n    --anchors anchors.hg38.tsv \\\n    --out results\u002F\u003Crun>\u002Freports\u002Fcohort_verify_anchors.tsv \\\n    --fail-on-fail\n```\n\nOr chained into the build driver:\n\n```bash\n# Freeze anchors at build time:\npython scripts\u002Fbuild_igvreports.py --samplesheet ... --anchors-mode generate \\\n    --anchors anchors.hg38.tsv\n\n# Verify a later build against frozen anchors:\npython scripts\u002Fbuild_igvreports.py --samplesheet ... --anchors-mode verify \\\n    --anchors anchors.hg38.tsv --fail-on-fail\n```\n\nAnchors TSV schema (`#`-prefixed header per lab BED convention):\n\n```\n#sample\ttrack_name\ttrack_type\tchrom\tstart\tend\texpected\ttolerance\tmin\tmax\tnotes\n```\n\n`track_type` is one of:\n- `bam` — `expected` is the count from `samtools view -c -F 1536` against\n  the source BAM at generate time, and the same count against the\n  embedded BAM slice at verify time. Default when the column is absent\n  (backwards compat — pre-2026-05-19 anchor files keep working).\n- `bedgraph` — `expected` is the number of data rows in the source\n  bedGraph overlapping the region (CpG count for methylation data,\n  peak count for ChIP coverage). Verify-time count comes from the\n  wig\u002FbedGraph slice embedded by igv-reports in the HTML — gzip-decoded\n  in-memory, no samtools needed.\n\nbedGraph tracks come from the samplesheet's `extra_tracks` column.\nAnchors for them are generated automatically alongside BAM anchors when\nyou run `verify_anchors.py generate` against a samplesheet that includes\nbedGraph entries (e.g. `*.5mC.bedgraph`, `*.5hmC.bg`, plain or `.gz`).\n\n`tolerance` is a ratio (default 5%). `min`\u002F`max` are absolute bounds that\noverride tolerance when set — useful for known-positive sites like\n\"this integration must have ≥20 reads\" or \"this promoter must have ≥10 CpGs\".\n\nsamtools is resolved in this order: `--samtools-sif PATH` → `$SAMTOOLS_SIF`\n→ `\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fcontainers\u002Fsamtools_v1.23.1.sif` →\nPATH `samtools`. SIF preferred per `rules\u002Fapptainer_vs_conda.md`.\nbedGraph anchors don't require samtools.\n\n**Why this matters for methylation viewers**: the silent-failure mode for\nmethylation reports is \"region rendered, slice has 0 CpGs\" — an empty\nbedGraph slice because the source had no calls in that window, or\nbecause the slice extraction silently dropped them. Pure structural\nverification (`verify_cohort.py`) confirms the bedGraph track is in the\nHTML but can't tell whether it's empty. The bedgraph-anchor mode closes\nthis gap.\n\n**Why opt-in and not default:** the verify step shells out to samtools per\n(sample × region) and indexes each slice — ~1 s\u002Fanchor. For a 6-sample\ncohort × 50 regions that's ~5 min on top of the structural verify (which\nruns in seconds). Reach for this when sample swap or content regression\nis a real concern; the structural verifier is sufficient for routine builds.\n\nWorked regression: `tests\u002Fintegration\u002Fanchor_verify\u002Fscenarios.sh` builds a\n2-sample cohort and asserts each of four content scenarios (tolerance\nviolation, min-bound violation, corrupted slice, missing anchor) triggers\nthe expected PASS \u002F FAIL \u002F SKIP outcome.\n\n### Triage interpretation (`interpret_reports.py`) — human-first reading surface\n\nAfter `verify-cohort` produces a checks TSV, `interpret_reports.py` rolls it\nup into a single cohort-wide `interpretation.md`: a summary table (sample ×\nverdict counts) then per-sample sections with a per-region verdict.\n\n```bash\npython scripts\u002Finterpret_reports.py \\\n    --checks results\u002F\u003Crun>\u002Freports\u002Fcohort_verify_anchors.tsv \\\n    --out    results\u002F\u003Crun>\u002Freports\u002Finterpretation.md\n```\n\nPer-region verdict, derived **solely** from the anchor results (no new\nthresholds):\n\n- `PASS` — every non-SKIP anchor for the region passed\n- `FAIL` — every non-SKIP anchor failed\n- `REVIEW` — mixed pass\u002Ffail across the region's tracks\n- `UNVERIFIED` — every anchor was SKIP (region not rendered, or no tracks\n  matched) — distinct from FAIL: nothing was checked\n\nRegions sort FAIL-first (FAIL → REVIEW → UNVERIFIED → PASS) so the ones\nneeding attention sit on top of each sample section; FAIL\u002FREVIEW\u002FUNVERIFIED\nget a per-track breakdown (observed vs expected + the reason), PASS collapses\nto one line. If the checks TSV is missing or empty, a fallback file points\nthe reader at the HTML instead. It's a reporting tool — it always exits 0 on a\nsuccessful write; `verify_anchors --fail-on-fail` remains the CI gate.\n\nWorked regression: `tests\u002Fintegration\u002Finterpret\u002Fscenarios.sh` renders a\nsynthetic cohort and asserts the summary totals, FAIL-first ordering, the\nmalformed-input exit-2 path, and the missing-input fallback.\n\n## Output and workflow logging\n\nEvery run logs to `logs\u002Frun_\u003CYYYYMMDD_HHMMSS>.log` next to the reports dir.\nThe log captures:\n- Resolved track paths (per genome, after databases_config.yaml lookup).\n- The exact `create_report` command.\n- The flanking value used (default **300 bp** — this is the value that's\n  baked into all the embedded data slices, so audit trails depend on it).\n- Per-region embedded data sizes (extracted post-render so the user can\n  see which regions inflated the HTML).\n- Total HTML size.\n\nThis satisfies CLAUDE.md §\"Logging and Audit Trail\" — every run is\nreproducible from the log alone.\n\n## Track choice nuances\n\nFor gencode on hg38, the default points at\n`gencode.v47.annotation.gff3.gz` (full annotation, bgzip + tabix). This\ngives transcript models with exons \u002F CDS \u002F UTRs. The gene-level-only\ncompanion (`gencode.v47.genes.annotation.sorted.gff3.gz`) renders only\nsolid gene boxes and is fine for high-zoom views, but the full annotation\nis the right default for read-level inspection at integration \u002F fusion \u002F\nSV junctions.\n\nFor mouse genomes, `databases_config.yaml` ships `.gtf.gz` paths instead.\nGTFs work in igv-reports if bgzip + tabix-indexed; **prep-track** converts\nplain-gzip GTFs the same way it does GFF3s.\n\nFor T2T-CHM13, only the FASTA + GTF + CGI are indexed in our DB; rmsk is\nabsent and is auto-skipped by the driver. The variant table will load\nwithout rmsk; flag this in the run log.\n\n## Common-case examples\n\nThe `examples\u002F` directory has runnable templates:\n\n- `single_sample.sh` — one BAM + one VCF + a sites BED → one HTML.\n- `cohort_samplesheet.sh` — TSV-driven multi-sample run.\n- `prep_track_demo.sh` — convert a plain-gzip gencode to bgzip+tabix.\n- `methylation_ont\u002F` — ONT 5mC\u002F5hmC viewer (BAM with `colorBy: basemod2`\n  + per-sample bedGraph at fixed y-axis 0..100). End-to-end worked\n  example with pre-sliced data; recipe.md explains the slots.\n\nThese are reference implementations; copy and edit them for new runs\nrather than starting from scratch.\n\n## Tests\n\nThree-layer suite under `tests\u002F`, orchestrated by `tests\u002Frun_all.sh`:\n\n| Layer | What it covers | Runtime | Needs |\n|---|---|---|---|\n| **unit** (`tests\u002Funit\u002F`) | 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 |\n| **smoke** (`tests\u002Fsmoke\u002F`) | `samtools_count` \u002F `samtools_index` \u002F full slice-decode-and-count round-trip against the committed `tests\u002Ffixtures\u002Ftiny_colo829.hg38.bam` (457 KB, sliced from public ONT COLO829 release) | ~3 s | pytest + samtools (SIF or PATH) |\n| **integration** (`tests\u002Fintegration\u002F`) | end-to-end: build a 2-\u002F3-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 |\n\n```bash\nbash tests\u002Frun_all.sh                  # all three layers\nbash tests\u002Frun_all.sh --unit-only      # ~1 s — fastest feedback loop\nbash tests\u002Frun_all.sh --no-integration # ~12 s — works on any machine\nbash tests\u002Frun_all.sh --integration-only\n```\n\nThe fixture provenance + regeneration recipe live in\n[tests\u002Ffixtures\u002FREADME.md](tests\u002Ffixtures\u002FREADME.md). Anchor counts the\nsmoke layer expects (chr2=5, chr7=9) are the contract — any fixture\nregeneration that changes them must also update the smoke test constants.\n\n## ONT methylation viewers (specialized path)\n\nFor per-read 5mC\u002F5hmC visualization the positional `--tracks` API does\nnot work — you need named tracks with `colorBy: \"basemod2\"` on the BAMs\nand `min: 0, max: 100` on the bedGraph tracks (cross-sample y-axis lock,\nsee `rules\u002Figv.md`). Use the `--track-config \u003Cjson>` passthrough:\n\n```bash\n# 1. Write a YAML spec listing samples (see tracks_spec.example.yaml).\n# 2. Generate tracks.json with the right defaults baked in:\npython scripts\u002Fgenerate_tracks_json.py \\\n    --spec tracks_spec.yaml --run-dir results\u002F\u003Crun>\u002F \\\n    --out results\u002F\u003Crun>\u002Ftracks.json\n\n# 3. Build the report:\npython scripts\u002Fbuild_igvreports.py \\\n    --sites results\u002F\u003Crun>\u002Fsites.hg38.bed \\\n    --track-config results\u002F\u003Crun>\u002Ftracks.json \\\n    --genome hg38 --flanking 0 \\\n    --type mutation --info-columns name \\\n    --output results\u002F\u003Crun>\u002Fmethylation_report.hg38.html\n```\n\n### Annotation shortcuts in the YAML\n\nThe default `--tracks` path (SV\u002Fvariant viewers) auto-resolves CpG islands,\ngencode, and RepeatMasker from `databases_config.yaml` when you pass\n`--genome hg38`. On the `--track-config` (methylation) path you used to\nhave to hand-paste those paths into the YAML. As of the methylation-polish\nround, you can use a `default:` shortcut for the same resolution:\n\n```yaml\ngenome: hg38\n\nannotation:\n  # SHORTCUT — resolved from databases_config.yaml for the genome above.\n  # Gets an Okabe-Ito color + sensible displayMode you can override per entry.\n  - default: gencode\n  - default: cgi\n  - default: repmasker\n  - default: epdnew_coding         # hg38 only\n  - default: epdnew_noncoding      # hg38 only\n\n  # Mix with EXPLICIT entries when needed (e.g. a pre-sliced custom track):\n  - name: \"My custom peak set\"\n    url: peaks\u002Fpromoter_slices.bed\n    format: bed\n```\n\nValid `default:` keys: `cgi`, `gencode`, `repmasker`, `epdnew_coding`,\n`epdnew_noncoding`. Mixing both forms is supported; order is preserved.\nOverride the canned `name`\u002F`color`\u002F`displayMode` per entry by adding the\nfield alongside `default:`. The shortcut needs a top-level `genome:` in\nthe spec and reads `databases_config.yaml` (resolution path same as the\nSV viewer driver — points at `$IGV_REPORTS_DB_CONFIG` if set, else the\nlab default; override per-run with `--db-config PATH`).\n\nKey methylation-specific defaults:\n- `--flanking 0` (sites BED already encodes the window — promoter\u002Fgene span).\n- `--info-columns name` (surface the BED `name` column in the variant table).\n- `--type mutation` (one-locus view per row; not split-screen).\n- bedGraph not bigwig — igv-reports cannot slice `.bw` directly.\n\nWhen `--track-config` is set the driver bypasses the auto-resolved\ndefault annotation tracks (CGI \u002F gencode \u002F rmsk) and the `--bam` \u002F\n`--vcf` \u002F `--extra-track` flags — the JSON is the source of truth.\nBuild annotation slices into the JSON instead.\n\n**`--apptainer` is auto-detected**: the driver flips to the dedicated\nigv-reports 1.16.0 SIF (`\u002Fdata1\u002Fgreenbab\u002Fusers\u002Fahunos\u002Fapps\u002Fcontainers\u002Figv-reports_1.16.0.sif`,\n83 MB, pulled from Galaxy depot) when `SLURM_JOB_ID` is in the\nenvironment — i.e. running on a compute node where the NFS conda\ncold-start tax matters (`rules\u002Fapptainer_vs_conda.md`). On the login\nnode it stays on the conda env. Override with `--apptainer` \u002F\n`--no-apptainer`; the decision lands in the run log.\n\nFull recipe and rationale: `references\u002Fmethylation_ont.md`. Worked\nexample with real data: `examples\u002Fmethylation_ont\u002F`.\n\n## Exporting HTML and PNG side-by-side (`--also-png`)\n\nThe HTML report is the deep-dive view; sometimes you also need static\nPNGs you can email, drop in a Slack channel, or paste into slides. The\ndriver's `--also-png` flag invokes the sister `igver` skill against the\n**same sites BED and same track list** that drove `create_report`, so\nboth artifacts cover identical regions with matching content.\n\n```bash\npython scripts\u002Fbuild_igvreports.py \\\n    --samplesheet samplesheet.tsv \\\n    --genome hg38 \\\n    --output-dir results\u002Frun\u002Freports\u002F \\\n    --jobs 6 \\\n    --also-png \\\n    --png-dpi 600 --png-display-mode collapse\n```\n\nOutput layout per sample:\n\n```\nresults\u002Frun\u002Freports\u002F\n├── \u003Csample>.hg38.html              # interactive\n├── png_\u003Csample>.hg38\u002F\n│   ├── igver_regions.bed           # flanked BED with UIDs (igver -r)\n│   ├── igver_input.txt             # track paths, one per line (igver -i)\n│   ├── manifest.tsv                # bridge: BED row ↔ PNG ↔ HTML row\n│   └── png\u002F\n│       ├── chr1-100-500.alpha.png  # one PNG per region\n│       └── chr2-0-700.beta.png\n└── index.html\n```\n\n### How consistency is guaranteed — five levers\n\n1. **Single sites BED with `--flanking` baked in.** The driver writes\n   `igver_regions.bed` with `start − flanking` and `end + flanking`\n   already applied (clamped to 0 on the low side); igver sees the same\n   coordinates create_report's igv.js viewer slices to.\n2. **Single resolved track list.** On the default (positional) path the\n   exact `[BAMs, VCF, extras, defaults]` list passed to `create_report` is\n   also written to `igver_input.txt`. On the `--track-config` path the\n   local-path `url:` entries from the JSON are extracted (http(s) URLs are\n   skipped — igver can't consume them).\n3. **Matched display mode.** Default is `--png-display-mode collapse` to\n   line up with the HTML's `BAM_DEFAULTS displayMode: COLLAPSED`. Override\n   to `expand` for per-read SV inspection on both artifacts.\n4. **UID-based filenames.** The BED's `name` column (auto-assigned\n   `region_\u003Cidx>` when missing) becomes both the HTML table label (via\n   `--info-columns name`) and the PNG filename suffix\n   (`\u003Cchr-start-end>.\u003Cuid>.png`). A user finds the same region in either\n   artifact by the same string.\n5. **`manifest.tsv` audit trail.** Per-sample TSV with columns:\n   `bed_row_idx, uid, chrom, start_orig, end_orig, start_flanked,\n   end_flanked, region, png_path, html_path, html_table_row`. One row\n   per region in BED order. `verify_cohort.py` reads this to run three\n   PNG-side checks (count matches, exist + non-empty, html-row contiguity).\n\n### Resolution of the `igver` invocation\n\nOrder, first match wins:\n1. `--igver-cmd '...'` (split on whitespace — supports `apptainer exec ... igver`).\n2. `$IGVER_CMD` env var (same shape).\n3. `igver` on PATH.\n4. Lab apptainer SIF at `\u002Fdata1\u002Fgreenbab\u002Fsoftware\u002Fimages\u002Figver_latest.sif`\n   (only if reachable).\n\nIf none resolve, the build exits before invoking create_report so you\ndon't pay the HTML cost before finding out PNGs are unavailable.\n\n### Methylation caveat (bigwig vs bedGraph)\n\nThe HTML methylation path uses **bedGraph** tracks (igv.js consumes\nthose directly); igver's per-read methylation view uses **BAMs** with\n`--color-by BASE_MODIFICATION`, and igver's cross-sample comparison view\nuses **bigwig** tracks. Content can be made identical only if both\nformats trace back to the same `modkit pileup` output (`modkit bedmethyl\ntobigwig` of the same bedGraph). The driver's `--also-png` passes the\nJSON's `url:` entries through verbatim, so if your YAML lists bedGraphs\nthey'll go to igver as-is — igver will render them but the result may\nlook different from the HTML's color-coded per-read view. For\npublication-quality methylation PNGs, supply a parallel `tracks.json`\nthat lists bigwigs and run `igver` separately (see `rules\u002Figv.md`).\n\nFor SV\u002Fvariant viewers this caveat doesn't apply — both render the\nidentical BAMs and the result is content-equivalent.\n\n### Cross-artifact verification\n\nThe driver runs an **inline existence check** right after igver returns:\nwalks each expected PNG path (`\u003Cchr>-\u003Cstart>-\u003Cend>.\u003Cuid>.\u003Cext>` derived\nfrom the manifest) and fails the build with an actionable message if\nany are missing or zero-byte. This catches igver's documented\nsilent-exit-0 failure mode (egg-link install without the IGV Java\nbinary; see `rules\u002Figv.md`) — `proc.returncode != 0` alone misses it.\n\nIn addition, `verify_cohort.py` then runs three checks per sample:\n\n| Check | Catches |\n|---|---|\n| `png_count_matches_bed` | partial igver run (SIGKILL mid-batch), stale manifest from a previous build, filename collisions |\n| `pngs_exist_and_nonempty` | empty IGV screenshots (\u003C 10 KB threshold; useful screenshots are typically ≥ 50 KB) |\n| `png_html_row_alignment` | manifest rows referencing a different HTML, html_table_row not contiguous 1..N |\n\n`--png-min-size-kb 5.0` lowers the threshold if you have legitimate\nno-data regions where igver produces a near-empty PNG.\n\n## Credits\n\nThe HTML rendering engine here is **not** this skill — it is the upstream\n[`igv-reports`](https:\u002F\u002Fgithub.com\u002Figvteam\u002Figv-reports) package\n(`create_report`) maintained by the IGV team at the Broad Institute (MIT,\n© 2018-2019 The Broad Institute and The Regents of the University of\nCalifornia). The rendered HTML embeds `igv.js` from the same team. This\nskill is a driver + verifier layer on top.\n\nWhen producing outputs that will end up in a publication, cite the igv.js\npaper: Robinson, J. T., Thorvaldsdóttir, H., Turner, D., & Mesirov, J. P.\n(2023). *igv.js: an embeddable JavaScript implementation of the\nIntegrative Genomics Viewer (IGV).* Bioinformatics, 39(1), btac830.\n[doi:10.1093\u002Fbioinformatics\u002Fbtac830](https:\u002F\u002Fdoi.org\u002F10.1093\u002Fbioinformatics\u002Fbtac830).\n\nFull attribution, the upstream MIT license verbatim, the reuse-vs-add\ntable, and BibTeX entries live in [`CREDITS.md`](CREDITS.md).\n\n## See also\n\n- `references\u002Fbest_practices.md` — full create_report flag reference,\n  format gotchas, performance notes. Read this if a run fails in a way\n  not listed in the Pitfalls table above.\n- `references\u002Fdatabases_config_paths.md` — per-genome track availability\n  matrix and exact YAML keys. Read this when adding a new genome or\n  diagnosing a missing-track warning.\n- `references\u002Fmethylation_ont.md` — ONT 5mC\u002F5hmC cheat-sheet (colorBy,\n  min:0\u002Fmax:100, flanking=0, bedGraph vs bigwig, EPDnew lookup).\n- `scripts\u002Fbuild_igvreports.py` — the driver. Reads `--samplesheet` or\n  `--bam\u002F--vcf` direct-args, resolves tracks, validates the sites BED,\n  writes the HTMLs and the run log. Supports `--track-config \u003Cjson>`\n  passthrough for fully-styled track sets.\n- `scripts\u002Fgenerate_tracks_json.py` — YAML spec → tracks.json with\n  ONT-methylation defaults baked in (colorBy=basemod2, min:0\u002Fmax:100,\n  group-paired Okabe-Ito colors).\n- `scripts\u002Fverify_report.py` — post-render structural verifier; parses\n  the HTML's embedded tableJson + sessionDictionary, confirms region\n  count \u002F coordinates \u002F track names match the inputs. Emits a verify.tsv\n  and gates on `--fail-on-fail`.\n- `scripts\u002Fverify_cohort.py` — cohort-level verifier; layered on top of\n  verify_report's per-sample checks, adds cross-sample contamination\n  scanning + index.html \u002F sample-id consistency. Auto-invoked at the end\n  of `build_igvreports.py --samplesheet`; standalone-runnable too.\n- `scripts\u002Fverify_anchors.py` — content verifier; samtools-counts the\n  embedded BAM slices and compares to anchors frozen from the source BAMs\n  at build time. Catches sample swaps that share basenames and silent\n  empty slices. Opt-in via `--anchors-mode generate|verify` on the build\n  driver; slow (~1 s\u002Fanchor). See SKILL.md content-verification section.\n- `scripts\u002Fprep_track.sh` — gunzip → sort → bgzip → tabix utility.\n- `igv-screenshots` skill — the **static PNG\u002FPDF\u002FSVG** counterpart based\n  on igver. Use it instead of this one when the deliverable is a\n  publication-quality figure rather than a clickable viewer.\n- Reference implementation:\n  `\u002Fdata1\u002Fgreenbab\u002Fprojects\u002Font\u002FProject_17424\u002Fresults\u002F20260503_hg38plusHTLV1EBV_cohort_integration_igvreports\u002F`\n  — 6-patient ATLL cohort viral-integration HTMLs + DNMT3A sanity check;\n  this skill was extracted from that work.\n",{"name":5,"description":7},"main","02b2e30e81b5a0f88e1ae70c562aeb7325172943","","https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill","https:\u002F\u002Fgithub.com\u002Fsahuno","https:\u002F\u002Fscholar.google.com\u002Fcitations?user=i-R6p6QAAAAJ&hl=en","Epigenetics, repeat elements, generative models & causal inference. @mskgreenbaumlab ","2026-05-21 09:10:49",{"source":8,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add sahuno\u002Figv-reports-skill --skill igv-reports",{"tool":39,"command":40},"bunx","bunx skills add sahuno\u002Figv-reports-skill --skill igv-reports",{"tool":42,"command":43},"pnpm","pnpm dlx skills add sahuno\u002Figv-reports-skill --skill igv-reports","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002FSKILL.md",true,[47,53,57,61,67,71,75,79,83,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149,153,157,161,165,169,173,177,181,185,189,193,197,201,205,209,213,217,221,225,229,233,237,241,245,249,253,257,261,265,269,273,277,281,285,289,293,297,301,305,309,313],{"path":48,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":51,"sourceHtmlUrl":52},".claude-plugin\u002Fmarketplace.json","REFERENCE","参考文档",1080,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002F.claude-plugin\u002Fmarketplace.json",{"path":54,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":55,"sourceHtmlUrl":56},".claude-plugin\u002Fplugin.json",1235,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002F.claude-plugin\u002Fplugin.json",{"path":58,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":59,"sourceHtmlUrl":60},".github\u002Fworkflows\u002Ftests.yml",1427,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Ftests.yml",{"path":62,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":65,"sourceHtmlUrl":66},".gitignore","OTHER","其他",73,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002F.gitignore",{"path":68,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":69,"sourceHtmlUrl":70},"CREDITS.md",5869,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002FCREDITS.md",{"path":72,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":73,"sourceHtmlUrl":74},"evals\u002Fevals.json",5050,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fevals\u002Fevals.json",{"path":76,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":77,"sourceHtmlUrl":78},"evals\u002Foptimizer.log",4885,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fevals\u002Foptimizer.log",{"path":80,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":81,"sourceHtmlUrl":82},"evals\u002Ftrigger_eval.json",3122,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fevals\u002Ftrigger_eval.json",{"path":84,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":87,"sourceHtmlUrl":88},"examples\u002Fmskcc\u002Fcohort_samplesheet.sh","SCRIPT","脚本",1435,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fcohort_samplesheet.sh",{"path":90,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":91,"sourceHtmlUrl":92},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FCpGIslands.promoter_regions.bed",96,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FCpGIslands.promoter_regions.bed",{"path":94,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":95,"sourceHtmlUrl":96},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FEPDnew.promoter_regions.bed",114,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FEPDnew.promoter_regions.bed",{"path":98,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":99,"sourceHtmlUrl":100},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FRepeatMasker.promoter_regions.bed",2598,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fannotation_slices\u002FRepeatMasker.promoter_regions.bed",{"path":102,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":103,"sourceHtmlUrl":104},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU59949.5hmC.bedgraph",20818,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU59949.5hmC.bedgraph",{"path":106,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":107,"sourceHtmlUrl":108},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU59949.5mC.bedgraph",22154,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU59949.5mC.bedgraph",{"path":110,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":111,"sourceHtmlUrl":112},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU61426.5hmC.bedgraph",20226,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU61426.5hmC.bedgraph",{"path":114,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":115,"sourceHtmlUrl":116},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU61426.5mC.bedgraph",22105,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829_PAU61426.5mC.bedgraph",{"path":118,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":119,"sourceHtmlUrl":120},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU59807.5hmC.bedgraph",20096,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU59807.5hmC.bedgraph",{"path":122,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":123,"sourceHtmlUrl":124},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU59807.5mC.bedgraph",21207,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU59807.5mC.bedgraph",{"path":126,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":127,"sourceHtmlUrl":128},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU61427.5hmC.bedgraph",19697,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU61427.5hmC.bedgraph",{"path":130,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":131,"sourceHtmlUrl":132},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU61427.5mC.bedgraph",20792,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbedgraph_slices\u002Fcolo829bl_PAU61427.5mC.bedgraph",{"path":134,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":135,"sourceHtmlUrl":136},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fbuild.sh",2383,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fbuild.sh",{"path":138,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":139,"sourceHtmlUrl":140},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fgencode.v47.promoter_regions.gtf.gz",3887,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fgencode.v47.promoter_regions.gtf.gz",{"path":142,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":143,"sourceHtmlUrl":144},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fgencode.v47.promoter_regions.gtf.gz.tbi",351,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fgencode.v47.promoter_regions.gtf.gz.tbi",{"path":146,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":147,"sourceHtmlUrl":148},"examples\u002Fmskcc\u002Fmethylation_ont\u002Frecipe.md",7281,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Frecipe.md",{"path":150,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":151,"sourceHtmlUrl":152},"examples\u002Fmskcc\u002Fmethylation_ont\u002Fsites.hg38.example.bed",102,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Fsites.hg38.example.bed",{"path":154,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":155,"sourceHtmlUrl":156},"examples\u002Fmskcc\u002Fmethylation_ont\u002Ftracks_spec.example.yaml",4085,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Ftracks_spec.example.yaml",{"path":158,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":159,"sourceHtmlUrl":160},"examples\u002Fmskcc\u002Fmethylation_ont\u002Ftracks.template.json",2219,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fmethylation_ont\u002Ftracks.template.json",{"path":162,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":163,"sourceHtmlUrl":164},"examples\u002Fmskcc\u002Fprep_track_demo.sh",880,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fprep_track_demo.sh",{"path":166,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":167,"sourceHtmlUrl":168},"examples\u002Fmskcc\u002FREADME.md",568,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002FREADME.md",{"path":170,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":171,"sourceHtmlUrl":172},"examples\u002Fmskcc\u002Fsingle_sample.sh",1025,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fmskcc\u002Fsingle_sample.sh",{"path":174,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":175,"sourceHtmlUrl":176},"examples\u002Fportable\u002Fcohort_samplesheet.sh",1489,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fportable\u002Fcohort_samplesheet.sh",{"path":178,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":179,"sourceHtmlUrl":180},"examples\u002Fportable\u002FREADME.md",706,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fportable\u002FREADME.md",{"path":182,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":183,"sourceHtmlUrl":184},"examples\u002Fportable\u002Fsingle_sample.sh",1583,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fexamples\u002Fportable\u002Fsingle_sample.sh",{"path":186,"resourceType":186,"resourceTypeDesc":187,"sizeBytes":188,"sourceHtmlUrl":16},"LICENSE","许可证",1069,{"path":190,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":191,"sourceHtmlUrl":192},"pyproject.toml",1355,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fpyproject.toml",{"path":194,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":195,"sourceHtmlUrl":196},"README.md",6829,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002FREADME.md",{"path":198,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":199,"sourceHtmlUrl":200},"references\u002Fbest_practices.md",7870,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Freferences\u002Fbest_practices.md",{"path":202,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":203,"sourceHtmlUrl":204},"references\u002Fdatabases_config_paths.md",3810,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Freferences\u002Fdatabases_config_paths.md",{"path":206,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":207,"sourceHtmlUrl":208},"references\u002Fmethylation_ont.md",9999,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Freferences\u002Fmethylation_ont.md",{"path":210,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":211,"sourceHtmlUrl":212},"scripts\u002Fbuild_igvreports.py",52609,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fbuild_igvreports.py",{"path":214,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":215,"sourceHtmlUrl":216},"scripts\u002Fgenerate_tracks_json.py",13388,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fgenerate_tracks_json.py",{"path":218,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":219,"sourceHtmlUrl":220},"scripts\u002Finterpret_reports.py",12917,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Finterpret_reports.py",{"path":222,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":223,"sourceHtmlUrl":224},"scripts\u002Fprep_track.sh",6057,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fprep_track.sh",{"path":226,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":227,"sourceHtmlUrl":228},"scripts\u002Fverify_anchors.py",34321,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fverify_anchors.py",{"path":230,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":231,"sourceHtmlUrl":232},"scripts\u002Fverify_cohort.py",28693,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fverify_cohort.py",{"path":234,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":235,"sourceHtmlUrl":236},"scripts\u002Fverify_report.py",16497,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fscripts\u002Fverify_report.py",{"path":238,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":239,"sourceHtmlUrl":240},"tests\u002F.gitignore",313,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002F.gitignore",{"path":242,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":243,"sourceHtmlUrl":244},"tests\u002Ffixtures\u002Fbuild_fixtures.sh",2797,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fbuild_fixtures.sh",{"path":246,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":247,"sourceHtmlUrl":248},"tests\u002Ffixtures\u002FREADME.md",2212,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002FREADME.md",{"path":250,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":251,"sourceHtmlUrl":252},"tests\u002Ffixtures\u002Ftiny_colo829.hg38.bam",466962,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Ftiny_colo829.hg38.bam",{"path":254,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":255,"sourceHtmlUrl":256},"tests\u002Ffixtures\u002Ftiny_colo829.hg38.bam.bai",86888,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Ftiny_colo829.hg38.bam.bai",{"path":258,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":259,"sourceHtmlUrl":260},"tests\u002Fintegration\u002Fanchor_verify\u002FREADME.md",4046,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fanchor_verify\u002FREADME.md",{"path":262,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":263,"sourceHtmlUrl":264},"tests\u002Fintegration\u002Fanchor_verify\u002Fscenarios.sh",8712,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fanchor_verify\u002Fscenarios.sh",{"path":266,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":267,"sourceHtmlUrl":268},"tests\u002Fintegration\u002Fcohort_verify\u002FREADME.md",3277,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fcohort_verify\u002FREADME.md",{"path":270,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":271,"sourceHtmlUrl":272},"tests\u002Fintegration\u002Fcohort_verify\u002Fscenarios.sh",7723,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fcohort_verify\u002Fscenarios.sh",{"path":274,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":275,"sourceHtmlUrl":276},"tests\u002Fintegration\u002Fend_to_end\u002FREADME.md",2676,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fend_to_end\u002FREADME.md",{"path":278,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":279,"sourceHtmlUrl":280},"tests\u002Fintegration\u002Fend_to_end\u002Fscenarios.sh",10001,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Fend_to_end\u002Fscenarios.sh",{"path":282,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":283,"sourceHtmlUrl":284},"tests\u002Fintegration\u002Finterpret\u002Fscenarios.sh",3823,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fintegration\u002Finterpret\u002Fscenarios.sh",{"path":286,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":287,"sourceHtmlUrl":288},"tests\u002Frun_all.sh",4219,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Frun_all.sh",{"path":290,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":291,"sourceHtmlUrl":292},"tests\u002Fsmoke\u002Ftest_slice_count.py",6310,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Fsmoke\u002Ftest_slice_count.py",{"path":294,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":295,"sourceHtmlUrl":296},"tests\u002Funit\u002Ftest_build_pngs.py",14017,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_build_pngs.py",{"path":298,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":299,"sourceHtmlUrl":300},"tests\u002Funit\u002Ftest_generate_tracks_json.py",8445,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_generate_tracks_json.py",{"path":302,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":303,"sourceHtmlUrl":304},"tests\u002Funit\u002Ftest_interpret_reports.py",13211,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_interpret_reports.py",{"path":306,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":307,"sourceHtmlUrl":308},"tests\u002Funit\u002Ftest_verify_anchors.py",20452,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_verify_anchors.py",{"path":310,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":311,"sourceHtmlUrl":312},"tests\u002Funit\u002Ftest_verify_cohort_png.py",6890,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_verify_cohort_png.py",{"path":314,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":315,"sourceHtmlUrl":316},"tests\u002Funit\u002Ftest_verify_report.py",10109,"https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Ftests\u002Funit\u002Ftest_verify_report.py",[318],{"skillNo":319,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":320,"categories":321,"keywords":322,"publishedAt":20},"01M733X19Q1REM02YJNZS10ZY2","https:\u002F\u002Fgithub.com\u002Fsahuno\u002Figv-reports-skill\u002Fblob\u002Fmain\u002Fskills\u002Figv-reports\u002FSKILL.md",[],[]]