Skip to content

feat: Add vcf_annotate_vepyr subworkflow - #13070

Merged
mwiewior merged 13 commits into
nf-core:masterfrom
mwiewior:add-vcf-annotate-vepyr
Oct 5, 2026
Merged

mwiewior merged 13 commits into
nf-core:masterfrom
mwiewior:add-vcf-annotate-vepyr

Conversation

@mwiewior

@mwiewior mwiewior commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Adds vcf_annotate_vepyr, an end-to-end VCF annotation subworkflow built on vepyr, a Rust reimplementation of Ensembl VEP.

It composes three existing modules:

Steps

  1. Download the cache (optional). When val_hf_repo is set (e.g. biodatageeks/vepyr_116_GRCh38_ensembl), the vepyr Parquet cache is downloaded from that Hugging Face dataset once and shared across all samples. Otherwise the local cache in ch_cache is used. Setting both, or neither, is an error. An optional revision and chromosome filters are passed through HUGGINGFACE_DOWNLOAD's ext.args.
  2. Normalise (optional, val_normalize). Runs bcftools norm. meta.yml documents the configuration used in vepyr's Ensembl VEP parity tests: split multiallelic records, no indel left-alignment. If no index is written, vepyr builds one.
  3. Annotate. vepyr annotate --everything emits [ meta, vcf.gz, tbi ].

Tests

All tests use the HG002 / Ensembl 116 chr22 fixtures from nf-core/test-datasets#2270:

  • normalise, with and without index generation, on an input with 14 multiallelic records. A setup step builds that input by joining input.vcf.gz with bcftools norm --multiallelics +both. The tests assert 1,000 biallelic output records and the same variantsMD5 as annotating input.vcf.gz without normalisation.
  • no normalise
  • Hugging Face cache shared across two samples (revision pinned, chr22 only), asserting a single download
  • failures when both or neither of ch_cache and val_hf_repo are set
  • stubs for the local cache and the Hugging Face path

Validation:

  • nf-test 0.9.5, Nextflow 26.04.6, --ci: 8/8 pass with --profile=+docker,arm64 and with --profile=+conda (osx-arm64).
  • Annotated outputs from both the local and the Hugging Face cache have the same record-body MD5, 1d4a92b815eb6193e6f4546f4ab35978, across 1,000 records.
  • nf-core subworkflows lint passes (35 passed, 0 warnings) and the prek hooks pass.
  • x64 Docker, Singularity and Conda run in upstream CI.

Generated by Claude Code

PR checklist

  • This comment contains a description of changes (with reason).
  • If you've fixed a bug or added code that should be tested, add tests!
  • Follow the component conventions in the contribution docs.
  • If necessary, include test data in your PR.
  • Remove all TODO statements.
  • Broadcast software version numbers to topic: versions (provided by the modules).
  • Follow the naming conventions.
  • Follow the parameters requirements.
  • Follow the input/output options guidelines.
  • Add a resource label (provided by the modules).
  • Use BioConda and BioContainers if possible to fulfil software requirements.
  • Ensure that the test works with either Docker / Singularity. Conda CI tests can be quite flaky:
    • nf-core subworkflows test vcf_annotate_vepyr --profile docker
    • nf-core subworkflows test vcf_annotate_vepyr --profile singularity (upstream CI)
    • nf-core subworkflows test vcf_annotate_vepyr --profile conda (upstream CI)

Compose optional bcftools normalisation with vepyr annotation and add published-fixture tests for indexed, unindexed, bypass and stub paths.

Generated by Codex
@mwiewior
mwiewior marked this pull request as ready for review October 1, 2026 10:19
@mwiewior
mwiewior marked this pull request as draft October 1, 2026 10:19
@mwiewior mwiewior changed the title Add vcf_annotate_vepyr subworkflow feat: Add vcf_annotate_vepyr subworkflow Oct 1, 2026
mwiewior and others added 5 commits October 2, 2026 12:42
Add an optional repository input that downloads one shared cache before annotation, retaining the existing local-cache path. Document revision and contig filters. Exercise local-cache bypass, remote stubs and a real two-sample annotation using the published chr22 cache.

Generated by Codex
Load ./nextflow.config only in the tests that rely on its ext.args
(BCFTOOLS_NORM, HUGGINGFACE_DOWNLOAD), following the nf-core testing
spec and the review of nf-core#13071. The no-normalise test runs without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the UNTAR setup into the three local-cache tests so the Hugging
Face and stub tests no longer fetch and extract cache.tar.gz. Mention
the optional Hugging Face download in the meta.yml summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With no ext.prefix and meta.id equal to the input basename, the default
output name matches the staged input symlink, so bcftools norm rewrites
the source file through it. Add the input/output collision guard used
by bcftools/annotate, csq, filter, convert and pluginfilltags, in script
and stub, with module and vcf_annotate_vepyr regression tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mwiewior and others added 2 commits October 2, 2026 13:51
The input/output collision guard is a module fix, so it now lives in its
own PR. Keep the subworkflow regression test, which passes once nf-core#13076
is merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test asserts the bcftools/norm guard from nf-core#13076, which is not on
master yet, so it fails in CI. Restore the meta.yml wording that matches
the current module; both come back once nf-core#13076 is merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mwiewior
mwiewior marked this pull request as ready for review October 2, 2026 12:08
@mwiewior
mwiewior enabled auto-merge October 2, 2026 12:08
mwiewior and others added 2 commits October 2, 2026 17:41
…core#13076

bcftools/norm now defaults to ${meta.id}_norm and refuses colliding
prefixes, so drop the ext.prefix workaround from the documented config.
Add a test where the sample ID matches the input basename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@jonasscheid jonasscheid left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I suggest using the nf-core/agents AGENTS.md for future PRs

--> The AI disclosure doesn't match nf-core/agents. The commits say "Generated by Codex" plus a Claude co-author, and the PR body ends with a Claude Code footer. nf-core/agents asks for "Generated by {name}" placed above the checklist.

workflow VCF_ANNOTATE_VEPYR {
take:
ch_vcf // channel: [ val(meta), path(vcf), path(tbi) ] (tbi optional, pass [])
ch_cache // channel: [ val(meta2), path(cache) ] (unused when val_hf_repo is set, pass [[], []])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think more user friendly would be to throw an error if ch_cach and val_hf_repo are set

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cb18242. The subworkflow now fails with set either ch_cache or val_hf_repo, not both when both are set. For symmetry it also fails with no cache given, set ch_cache or val_hf_repo when neither is set; before, that case surfaced later as an unclear VEPYR_ANNOTATE error. Two new stub tests, cache and Hugging Face repo - fails and no cache - fails, assert workflow.failed and the message.

all contigs present in the VCF, for example:

```
withName: 'HUGGINGFACE_DOWNLOAD' {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-hint:

The Hugging Face path only works if the pipeline also sets ext.args = '--repo-type dataset' on HUGGINGFACE_DOWNLOAD. That flag is required, not optional, and nf-core guidance says required arguments should be inputs, not ext.args.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cb18242, without a new input. vepyr caches are always HF datasets, so the subworkflow passes datasets/<owner>/<repo> to HUGGINGFACE_DOWNLOAD (a datasets/ prefix already in val_hf_repo is kept). hf download 1.18.0, the version the module pins, resolves that ID without --repo-type; the bare ID is looked up as a model. Nothing required is left in ext.args, only the optional --revision/--include. I've removed --repo-type dataset from the test config and meta.yml. The real two-sample HF test still passes with the same variantsMD5.

tag "huggingface"
tag "huggingface/download"

test("homo_sapiens - vcf - normalise") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional:

The normalisation branch is never actually tested. (tests L18, tests L75) I checked the test data: input.vcf.gz has 1,000 records and none are multiallelic. Combined with --do-not-normalize, bcftools norm changes nothing.
That is why the normalise and no-normalise snapshots share the same variantsMD5 (fdddbf25…).
That MD5 is computed over full record lines, INFO included, so the annotation itself is covered. Only the split step is untested.
A test VCF with one or two multiallelic sites would fix this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cb18242. nf-core/test-datasets#2298 adds input_multiallelic.vcf.gz: the same HG002 window before normalisation, 986 records, 14 of them multiallelic. Both normalise tests now read it and assert 1,000 output records, all biallelic. Splitting it reproduces input.vcf.gz exactly, so the snapshots keep variantsMD5 fdddbf25…. That is the same as the no-normalise test, but now it shows split + annotate equals annotating the pre-split input; before, it was a no-op. The two tests need #2298 merged before they pass in CI; locally they pass against its branch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up in 643d2b1: per the suggestion on nf-core/test-datasets#2298, the normalise tests no longer need new test data. A setup step runs BCFTOOLS_NORM (aliased BCFTOOLS_JOIN_MULTIALLELICS) with --multiallelics +both --do-not-normalize on input.vcf.gz. That joins the 1,000 records into 986, 14 of them multiallelic, and the subworkflow splits them again. --do-not-normalize is needed because the module always passes --fasta-ref, and left-aligning fails on the VCF's chr22 vs the FASTA's 22. The task logs confirm 14 records joined in setup and 14 split in the subworkflow. Snapshots are unchanged, and the tests pass against the published test data.

}
}

test("homo_sapiens - vcf - normalise - sample id matches input basename") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "sample id matches input basename" test checks bcftools/norm behaviour, not subworkflow logic. (tests L231) That module already has its own test for it (from #13076). Dropping it here saves a full vepyr run in CI.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in cb18242, together with its snapshot.

script "modules/nf-core/untar/main.nf"
process {
"""
input[0] = Channel.of([

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

de-capitalize Channel. Wont work anymore with higher nextflow versions. Should be channel

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cb18242. Every Channel. in the tests is now channel..

@@ -0,0 +1,94 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json
name: "vcf_annotate_vepyr"
description: |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs and prose (nf-core/agents prose rules)

One sentence doesn't parse. (meta.yml L26-29) "The configuration vepyr's Ensembl VEP parity tests are validated on splits multiallelic records…" needs rewording. It also says the index "vepyr needs", but VEPYR_ANNOTATE builds the index itself when it is missing. Writing it in bcftools norm only saves a tabix step.
The ext.prefix paragraph describes module internals. (meta.yml L37-39) Under the rule to scope prose to the immediate task, it can go.
"Pipelines should default it to true" appears three times. Keep one. The three places are:
meta.yml L25, in the description
meta.yml L78, in the val_normalize input
main.nf L16, as a comment
The constraints on ch_fasta and ch_cache aren't documented. (meta.yml L57, meta.yml L62)
Both must be value channels. If a pipeline passes a queue channel, only the first sample gets annotated.
BCFTOOLS_NORM output must be bgzipped VCF. -Ob and -Ou produce BCF, and -Ov produces plain VCF, which makes vepyr fall back to --fork 1.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All addressed in cb18242:

  • Reworded the configuration sentence. --write-index=tbi is now described as optional: it only saves the tabix step, because VEPYR_ANNOTATE builds a missing index.
  • Removed the ext.prefix paragraph.
  • "Default it to true" now appears once, in the val_normalize description.
  • ch_cache and ch_fasta are documented as value channels (channel.value(...) or .collect()), noting that a queue channel annotates only the first sample.
  • Documented keeping BCFTOOLS_NORM output as bgzip VCF (--output-type z): VEPYR_ANNOTATE runs a single pipeline for BCF (-Ob/-Ou) and plain VCF (-Ov).

- Prefix val_hf_repo with datasets/, so HUGGINGFACE_DOWNLOAD needs no
  required --repo-type in ext.args; only --revision/--include remain.
- Fail when both ch_cache and val_hf_repo are set, or neither, and test
  both failures.
- Test normalisation on input_multiallelic.vcf.gz (986 records, 14
  multiallelic; nf-core/test-datasets#2298): the output has 1,000
  biallelic records and the same variantsMD5 as annotating the
  pre-split input.vcf.gz without normalisation.
- Drop the basename test (covered by the bcftools/norm module tests)
  and use channel instead of Channel.
- meta.yml: reword the bcftools norm configuration, drop the ext.prefix
  paragraph, keep one "default to true", document that ch_cache and
  ch_fasta must be value channels and that BCF or plain VCF output
  makes VEPYR_ANNOTATE run a single pipeline.

Generated by Claude Code
@mwiewior

mwiewior commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

@jonasscheid I've fixed the disclosure: the PR body now ends with Generated by Claude Code above the checklist, and the review commit (cb18242) ends its message with the same line, without the co-author trailer. I haven't rewritten the earlier commits because the PR is squash-merged. All six threads are answered. The two normalise tests need nf-core/test-datasets#2298 merged before CI passes.

Instead of a new test-datasets file (nf-core/test-datasets#2298), the
normalise tests join input.vcf.gz back into multiallelic records with
an aliased BCFTOOLS_NORM in setup: --multiallelics +both turns 1,000
records into 986, 14 of them multiallelic, and the subworkflow splits
them again. --do-not-normalize is needed because the module always
passes --fasta-ref, and left-aligning fails on chr22 vs 22. Snapshots
are unchanged.

Generated by Claude Code
mwiewior added a commit to biodatageeks/vepyr that referenced this pull request Oct 5, 2026
Sync the vcf_annotate_vepyr tests with nf-core/modules#13070 643d2b157:
the normalise tests join input.vcf.gz back into multiallelic records
with an aliased BCFTOOLS_NORM in setup, so the staged
input_multiallelic.vcf.gz (nf-core/test-datasets#2298) is no longer
needed. Revert its staging in stage_testdata.py and the harness hash,
run the aliased process under amd64 emulation like BCFTOOLS_NORM, and
drop the #2298 references from the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@jonasscheid jonasscheid left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mwiewior
mwiewior added this pull request to the merge queue Oct 5, 2026
Merged via the queue into nf-core:master with commit 7d5d5ab Oct 5, 2026
41 checks passed
@mwiewior
mwiewior deleted the add-vcf-annotate-vepyr branch October 5, 2026 06:36
mwiewior added a commit to biodatageeks/vepyr that referenced this pull request Oct 5, 2026
vcf_annotate_vepyr merged as nf-core/modules#13070 (7d5d5ab); the
mirror here is byte-identical to it. Install it with
`nf-core subworkflows install vcf_annotate_vepyr`, which also installs
vepyr/annotate, bcftools/norm and huggingface/download, instead of
copying it from this repository. Add an nf-core subworkflow badge next
to the module badge and mark the submission steps done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mwiewior added a commit to biodatageeks/vepyr that referenced this pull request Oct 5, 2026
…d badge (#167)

* nf-core: sync vcf_annotate_vepyr with nf-core/modules#13070

Mirror the subworkflow as it stands after the #13070 review round:
optional Hugging Face cache download (val_hf_repo, datasets/ prefix
added by the subworkflow), errors when both or neither of ch_cache and
val_hf_repo are set, normalization tests on a multiallelic input, and
the reworded meta.yml.

The dev harness follows: bcftools/norm pinned past #13076 (default
${meta.id}_norm prefix and collision guard), huggingface/download
fetched at the same commit and run under amd64 emulation, the parity
test passes the new seventh argument, and stage_testdata.py builds
input_multiallelic.vcf.gz from raw_chr22.vcf.gz, checked against the
md5 published in nf-core/test-datasets#2298.

Docs: nextflow.md drops the required ext.prefix, documents the
Hugging Face cache and value-channel inputs, and copies both helper
modules; the subworkflow diagram gains the download branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* nf-core: build the multiallelic test input in setup, as upstream

Sync the vcf_annotate_vepyr tests with nf-core/modules#13070 643d2b157:
the normalise tests join input.vcf.gz back into multiallelic records
with an aliased BCFTOOLS_NORM in setup, so the staged
input_multiallelic.vcf.gz (nf-core/test-datasets#2298) is no longer
needed. Revert its staging in stage_testdata.py and the harness hash,
run the aliased process under amd64 emulation like BCFTOOLS_NORM, and
drop the #2298 references from the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: advertise the merged nf-core subworkflow

vcf_annotate_vepyr merged as nf-core/modules#13070 (7d5d5ab); the
mirror here is byte-identical to it. Install it with
`nf-core subworkflows install vcf_annotate_vepyr`, which also installs
vepyr/annotate, bcftools/norm and huggingface/download, instead of
copying it from this repository. Add an nf-core subworkflow badge next
to the module badge and mark the submission steps done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants