Skip to content

Add LRSOMATICREPORT as the final pipeline step - #176

Open
ljwharbers wants to merge 23 commits into
devfrom
worktree-lrsomatic-report-module
Open

Add LRSOMATICREPORT as the final pipeline step#176
ljwharbers wants to merge 23 commits into
devfrom
worktree-lrsomatic-report-module

Conversation

@ljwharbers

@ljwharbers ljwharbers commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Wraps the lrsomatic_report R/Quarto tool as a new local module LRSOMATICREPORT that renders a self-contained per-sample HTML report, and runs it as the final pipeline step.

  • The tool is vendored, not a submodule. assets/lrsomatic_report is a vendored copy of v1.3.0 (236ab35); see assets/lrsomatic_report/VENDORED.md for the rationale, the exact provenance, and the re-sync procedure. nextflow run IntGenomicsLab/lrsomatic clones the pipeline but does not initialise submodules, so a submodule would leave the default --report_src pointing at an empty directory.
  • Runs after VEP-annotated somatic SNVs, Severus SVs (and their VEP annotation), ASCAT and Wakhan copy number, and QC — gated by --skip_report (default false), consistent with skip_ascat/skip_wakhan.
  • Every report input is optional; channels are joined on a plain sample-id string (not full meta maps) with remainder: true, so any skipped or missing upstream step degrades gracefully to a "not available" section instead of breaking the join.
  • New params: --skip_report, --report_src (default: the vendored path), --report_gene_panel (a comma-separated list of panels — see below).
  • Adds a solution_dirs output to the WAKHAN module so its per-solution copy-number plots can be staged into the report.

What the vendored v1.3.0 brings over the originally vendored v1.1.0

The vendored tree was re-synced v1.1.0v1.2.1v1.3.0 over the life of this PR. Nothing on the pipeline side had to change for the report features themselves — no new module inputs, no staging changes, no container rebuild (see below); the only pipeline-side change was making --report_gene_panel accept a list.

From v1.2.x:

  • A breakend circos plot, cross-linked to the SV table and redrawn as the table is filtered; one SV table row per rearrangement, with location columns read per SV type.
  • Gene panels ship per reference (lymphoid.{hg38,t2t}.tsv, and a new sarcoma panel) and are selected by their bare name, resolved against the detected reference. A coordinate-carrying panel must declare its reference; a mismatch is a hard error rather than a silently wrong filter. Coordinate matching is what makes breakend filtering reliable — whether a breakend carries a VEP gene symbol at all depends on the VEP invocation.
  • The variant table footnote names the file the VAF columns came from and how many rows they joined, and flags the consensus caveat below rather than leaving it invisible.
  • Phasing statistics moved into QC as a collapsible block.

New in v1.3.0:

  • Tickbox dropdown filters on the categorical columns of both variant tables.
  • The report's gene panel selector became checkboxes, so several panels can be active at once and the reader can retick them without re-rendering.
  • The tool's --gene-panel is now repeatable. On the pipeline side --report_gene_panel therefore takes a comma-separated list — builtin names and custom paths mixed freely — and the panels are unioned: a variant or SV is kept if it hits any of them. Panel values are validated at launch (validateReportGenePanels()) instead of failing inside the report task after alignment, calling and annotation have already run: none cannot be combined with a real panel, an unknown builtin name or a missing panel file is an error, and two panel files sharing a base name are rejected because they would collide in the task's staging directory.

Containers

The module pins two Wave builds from its own environment.yml (dependencies only — R, Quarto and the tool's R packages; the tool itself is vendored):

Engine Reference
Singularity oras://community.wave.seqera.io/library/r-base_quarto_r-base64enc_r-data.table_pruned:dc62d809aa6fd497
Docker community.wave.seqera.io/library/r-base_quarto_r-base64enc_r-data.table_pruned:c1049dbaf31bf178

Two separate builds are needed: wave --singularity produces a Singularity-native SIF (the oras:// reference), the default build a genuine OCI image. Rebuild both whenever environment.yml changes.

Neither re-sync changed environment.yml: a library()/require() grep over the upstream R/, bin/ and templates/ resolves to circlize, data.table, dplyr, DT, ggplot2, htmltools, optparse, quarto and yaml, all already pinned. Both images are unchanged.

Notable fixes made along the way

  • environment.yml was originally derived from the tool's README package list; grepping the actual R source showed GenomicRanges/ComplexHeatmap/tidyr are unused, so they were dropped. This also sidesteps a real bioconda/micromamba incompatibility — GenomeInfoDbData's post-link data-fetch script never runs under micromamba.
  • The Wave-built container doesn't auto-source conda's activate.d hooks (Quarto needs QUARTO_SHARE_PATH), so the module sources them explicitly. CONDA_PREFIX is exported first because one hook references it unguarded under set -u, but it now defaults to the existing value and only falls back to /opt/conda when unset — hard-coding it broke -profile conda.
  • Each --report_gene_panel entry is either a builtin panel name or a path to a TSV. Panel paths are declared as an optional path(gene_panels, stageAs: 'gene_panels/*') module input so Nextflow stages them and they are bound into the container; previously a path was interpolated into the command line as a bare string and the tool aborted with "--gene-panel not found". Each --gene-panel argument is also quoted, so panel paths containing spaces work.
  • HOME and TMPDIR are pointed at the task work dir so Quarto/Deno's cache and session directories can't depend on the container's $HOME or a read-only /tmp.

Known limitations

  • The somatic_vcf fed to the report is the final (possibly consensus) somatic VCF, staged under variants/phased/ purely to drive the report's run-mode detection and VAF column; if multiple somatic callers were combined via consensus, that VAF column won't reflect a single real caller. As of v1.2.0 the report makes this visible — the variant-table footnote names the source file and flags multi-caller rows — rather than hiding it. Joining per caller instead would require staging each caller's VCF, i.e. a change to this module's input tuple, so it was deliberately not done. The VEP-based variant table (the primary source) is unaffected.
  • Wakhan outputs are staged and located by fixed path; the report tool's own handling of them is still limited to what its v1.3.0 release supports.

Test plan

  • nextflow config . validates; --report_gene_panel resolves to the expected --gene-panel argument(s)
  • nextflow lint clean — no new warnings or errors introduced
  • pre-commit run --all-files clean
  • Module nf-test, stub
  • Module nf-test, real render under -profile singularity — renders a valid HTML report from a VEP somatic VCF
  • Module nf-test, real render with a user-supplied gene panel TSV — passes only if the panel file is genuinely staged into the container, which is the regression this PR fixes
  • Module nf-test, real render with several gene panels at once — one repeated --gene-panel flag per entry, builtin and file mixed
  • All four module tests re-run against the vendored v1.3.0 tree under -profile singularity; the only snapshot movement across the re-syncs was the version string
  • Full pipeline nf-test on CI (docker | 25.04.0 and docker | latest-everything)

🤖 Generated with Claude Code

robert-a-forsyth and others added 2 commits June 3, 2026 16:36
Wraps the lrsomatic_report R/Quarto tool (added as a git submodule) in a new
local module that renders a self-contained per-sample HTML report from the
pipeline's key outputs (VEP-annotated somatic SNVs, Severus SVs, ASCAT copy
number, QC). Runs last, gated by --skip_report, so future dependents (e.g. a
real Wakhan integration) can hook in without restructuring.

Every report input is optional and joined by plain sample-id keys (not full
meta maps) so missing/skipped upstream steps degrade gracefully instead of
breaking the join. The module's environment.yml was verified against the
actual R code (not just the tool's README) and trimmed to what's really used;
a Wave container was built and validated with a real end-to-end render.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings July 15, 2026 10:41
@github-actions

Copy link
Copy Markdown

This PR is against the main branch ❌

  • Do not close this PR
  • Click Edit and change the base to dev
  • This CI test will remain failed until you push a new commit

Hi @ljwharbers,

It looks like this pull-request is has been made against the IntGenomicsLab/lrsomatic main branch.
The main branch on nf-core repositories should always contain code from the latest release.
Because of this, PRs to main are only allowed if they come from the IntGenomicsLab/lrsomatic dev branch.

You do not need to close this PR, you can change the target branch to dev by clicking the "Edit" button at the top of this page.
Note that even after this, the test will continue to show as failing until you push a new commit.

Thanks again for your contribution!

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

nf-core pipelines lint overall result: Passed ✅ ⚠️

Posted for pipeline commit 45a59e6

+| ✅ 201 tests passed       |+
#| ❔  23 tests were ignored |#
#| ❔   1 tests had warnings |#
!| ❗  38 tests had warnings |!
Details

❗ Test warnings:

  • nextflow_config - Config manifest.version should end in dev: 1.1.0
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in lint_log.txt: Named file extensions MUST be emitted for ALL output channels
  • pipeline_todos - TODO string in lint_log.txt: List additional required output channels/values here
  • pipeline_todos - TODO string in README.md: Include a figure that guides the user through the major workflow steps. Many nf-core
  • pipeline_todos - TODO string in nextflow.config: Specify your pipeline's command line flags
  • pipeline_todos - TODO string in nextflow.config: Update the field with the details of the contributors to your pipeline. New with Nextflow version 24.10.0
  • pipeline_todos - TODO string in meta.yml: #Add a description of the module and list keywords
  • pipeline_todos - TODO string in nextflow.config: Specify any additional parameters here
  • pipeline_todos - TODO string in methods_description_template.yml: #Update the HTML below to your preferred methods description, e.g. add publication citation for this pipeline
  • pipeline_todos - TODO string in CONTRIBUTING.md: Add any pipeline specific contribution guidelines here, such as coding styles, procedures, checklists etc.
  • pipeline_todos - TODO string in base.config: Check the defaults for all processes
  • pipeline_todos - TODO string in base.config: Customise requirements for specific processes.
  • schema_description - Ungrouped param in schema: skip_modkit
  • schema_description - No description provided in schema for parameter: generate_gvcf
  • schema_description - No description provided in schema for parameter: autocorrelation
  • schema_description - No description provided in schema for parameter: vep_custom
  • schema_description - No description provided in schema for parameter: vep_custom_tbi
  • schema_description - No description provided in schema for parameter: severus_minsupport
  • schema_description - No description provided in schema for parameter: wakhan_chroms
  • local_component_structure - phasing_haplotyping.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - small_variant_consensus.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_reference_files.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - prepare_annotation.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure
  • local_component_structure - deepsomatic.nf in subworkflows/local should be moved to a SUBWORKFLOW_NAME/main.nf structure

❔ Tests ignored:

  • files_exist - File is ignored: CODE_OF_CONDUCT.md
  • files_exist - File is ignored: assets/nf-core-lrsomatic_logo_light.png
  • files_exist - File is ignored: docs/images/nf-core-lrsomatic_logo_light.png
  • files_exist - File is ignored: docs/images/nf-core-lrsomatic_logo_dark.png
  • files_exist - File is ignored: .github/ISSUE_TEMPLATE/config.yml
  • files_exist - File is ignored: .github/workflows/awstest.yml
  • files_exist - File is ignored: .github/workflows/awsfulltest.yml
  • files_exist - File is ignored: .github/CONTRIBUTING.md
  • nextflow_config - Config variable ignored: manifest.name
  • nextflow_config - Config variable ignored: manifest.homePage
  • files_unchanged - File ignored due to lint config: .gitattributes
  • files_unchanged - File ignored due to lint config: CODE_OF_CONDUCT.md
  • files_unchanged - File ignored due to lint config: .github/ISSUE_TEMPLATE/bug_report.yml
  • files_unchanged - File ignored due to lint config: .github/PULL_REQUEST_TEMPLATE.md
  • files_unchanged - File ignored due to lint config: .github/workflows/branch.yml
  • files_unchanged - File ignored due to lint config: .github/workflows/linting_comment.yml
  • files_unchanged - File ignored due to lint config: .github/workflows/linting.yml
  • files_unchanged - File ignored due to lint config: assets/email_template.txt
  • files_unchanged - File ignored due to lint config: assets/nf-core-lrsomatic_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-lrsomatic_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-lrsomatic_logo_dark.png
  • files_unchanged - File ignored due to lint config: docs/README.md
  • actions_awstest - 'awstest.yml' workflow not found: /home/runner/work/lrsomatic/lrsomatic/.github/workflows/awstest.yml

❔ Tests fixed:

✅ Tests passed:

Run details

  • nf-core/tools version 4.0.2
  • Run at 2026-09-03 13:09:43

Copilot AI 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.

Pull request overview

Adds a new final pipeline step (LRSOMATICREPORT) to render a per-sample, self-contained HTML report from the pipeline’s main outputs, controlled via --skip_report and report-related parameters.

Changes:

  • Integrates LRSOMATICREPORT as the final workflow step, joining optional upstream outputs to degrade gracefully when steps are skipped.
  • Introduces a new local module (modules/local/lrsomaticreport) with conda/container support and nf-test coverage.
  • Adds new report parameters to nextflow.config, nextflow_schema.json, and updates documentation and module configuration/publishing.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
workflows/lrsomatic.nf Wires LRSOMATICREPORT into the main workflow and assembles/join report inputs.
nextflow.config Adds skip_report, report_src, and report_gene_panel defaults.
nextflow_schema.json Exposes report parameters in the schema (report_options) and adds skip_report.
conf/modules.config Adds publishing configuration and parameter-to-args wiring for LRSOMATICREPORT.
modules/local/lrsomaticreport/main.nf Implements the report-rendering process (staging inputs, running render_report.R).
modules/local/lrsomaticreport/environment.yml Defines the conda environment used for the module.
modules/local/lrsomaticreport/meta.yml Adds nf-core style metadata for the new module.
modules/local/lrsomaticreport/tests/main.nf.test Adds nf-test coverage (stub + real render) for the module.
modules/local/lrsomaticreport/tests/main.nf.test.snap Snapshot output for the stub test.
docs/usage.md Documents --skip_report, --report_src, and --report_gene_panel.
docs/output.md Documents the new per-sample report/ output directory and HTML artifact.
.gitmodules Adds the assets/lrsomatic_report submodule definition.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread conf/modules.config Outdated
Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread docs/usage.md Outdated
Comment thread workflows/lrsomatic.nf Outdated
@ljwharbers
ljwharbers changed the base branch from main to dev July 15, 2026 14:10
CI's docker/singularity 25.04.0 jobs regressed vs. dev after adding the
report step. Chasing the failures end-to-end (real render, not just
config) surfaced five distinct bugs:

1. workflows/lrsomatic.nf: normal-sample QC was keyed by the boolean
   meta.paired_data instead of meta.id, so the join produced a
   malformed remainder tuple and crashed the pipeline for any matched
   tumor/normal pair reaching the report step. Also fixed misleading
   comments (paired_data is not a sample id).

2. modules/local/lrsomaticreport/main.nf: qc_tumor_files/qc_normal_files
   were staged flat. mosdepth/samtools default to a meta.id-only
   prefix, so a matched pair's tumor and normal QC files share a name
   and collided in the task work dir. Fixed via stageAs subdirectories
   (qc_tumor/*, qc_normal/*) with basename-based destination linking.

3. modules/local/lrsomaticreport/environment.yml: missing r-r.utils,
   needed by data.table::fread() to read a gzipped VCF directly --
   only surfaced once a real Severus VCF reached the render step.
   Required rebuilding the Wave container (new frozen tag
   4506737a6b63b769); the container directive now follows this
   codebase's existing dual-engine pattern (singularity blob URL +
   docker tag, e.g. modules/local/bcftools/view/main.nf) since the
   frozen singularity artifact is SIF/ORAS-native, not a portable OCI
   image.

4. assets/lrsomatic_report submodule (re-pinned to fdf2a0a):
   parse_severus_vcf's fread() errored instead of returning zero rows
   when a sample's Severus VCF has no variant records at all (skip
   landing exactly on the last line). Fixed upstream with tryCatch.

5. modules/local/lrsomaticreport/main.nf: report_src was staged via a
   shared symlink (same fixed path for every sample), and Quarto
   renders in-place next to the .qmd. Concurrent per-sample tasks
   raced on that single physical directory ("cannot open file
   per_sample.qmd"). Fixed with stageInMode 'copy' for an isolated
   copy per task.

Snapshot regenerated (additive: LRSOMATICREPORT versions entry +
sample*/report/*_report.html); tests/.nftignore excludes the
Quarto-rendered HTML's unstable content, matching multiqc/nanoplot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 16, 2026 17:21

Copilot AI 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.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comment thread modules/local/lrsomaticreport/meta.yml Outdated
Comment thread nextflow.config
Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread modules/local/lrsomaticreport/main.nf Outdated
The previously pinned tag (4506737a6b63b769) was built during a
singularity.enabled=true Wave session, which only produces a
Singularity-native SIF artifact -- Docker CI's docker|25.04.0 job
failed to pull it ("Encountered remote
application/vnd.sylabs.sif.config.v1+json (unknown) when fetching").

A second Wave freeze build under a docker-context session
(docker.enabled=true, wave.strategy=['conda']) produced tag
9d12b9297c3c4d38, a genuine OCI image (verified via
`skopeo inspect --raw`: application/vnd.oci.image.manifest.v1+json
with real tar+gzip layers). The dual-engine container directive now
uses this new tag for the docker branch; the singularity branch's
blob URL is unchanged (already confirmed working).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 07:30

Copilot AI 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.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread modules/local/lrsomaticreport/main.nf Outdated
stageInMode 'copy' (added to fix a race condition where concurrent
per-sample Quarto renders collided on a shared symlinked report_src
directory) has a real bug in Nextflow 25.04.0 for directory-type path
inputs under the docker executor: docker|latest-everything passed but
docker|25.04.0 failed with "cannot open file
lrsomatic_report/bin/render_report.R: No such file or directory" --
the copied directory came out incomplete.

Replaced the process-level directive with a plain `cp -rL` in the
script body itself. This is pure shell with no Nextflow-version
dependency, and fixes the same underlying problem: each task now
dereferences report_src into its own private, task-local copy before
Quarto renders in-place next to the .qmd.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 09:17

Copilot AI 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.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread modules/local/lrsomaticreport/main.nf Outdated
Comment thread conf/modules.config
Comment thread modules/local/lrsomaticreport/main.nf Outdated
…Wakhan

The `assets/lrsomatic_report` submodule could not reach anyone. `nextflow run
IntGenomicsLab/lrsomatic` clones the pipeline repo but not its submodules, and
CI checks out without `submodules: recursive` -- so the gitlink resolved to an
empty directory for end users and for every CI run, which is what has been
failing PR #176. Replace it with the upstream tree as real tracked files
(bin/, R/, templates/, assets/, LICENSE, README.md; ~565 KB), recorded in
assets/lrsomatic_report/VENDORED.md. `--report_src` stays, now as an override
for a local checkout rather than a required setup step.

Dependencies stay in the Wave multi-package container, rebuilt from the
module's environment.yml after adding r-base64enc (used by R/utils.R
embed_png(), listed in the upstream recipe, missing here).

The tool is at v1.1.0, several releases past the pin. Rewire accordingly:

- Drop the symlink tree that faked variants/clairs vs variants/clairsto so the
  old CLI could infer run mode. v1.1.0 derives the mode from whether normal-side
  QC is present and discovers files recursively by base name, so staging is now
  flat plus three fixed locations (qc/tumor, qc/normal, wakhan).
- Drop `cp -rL` of report_src: render_report.R copies templates/ and assets/
  into a task-local ._render itself, so the shared source dir is never written.
- Feed the phased somatic VCF rather than the pre-phasing caller VCF, at the
  path the tool looks for it. The VAF/depth/phase-set columns now come from the
  same file VEP annotated instead of a possibly-consensus VCF.
- Add SV_VEP.out.vcf, the tool's primary SV annotation source.
- Add the Wakhan outputs it renders. Its per-solution plots all share one base
  name, so WAKHAN gains a `solution_dirs` output and the directories are staged
  whole rather than the files individually.
- Export TMPDIR into the task work dir alongside HOME. Quarto's Deno runtime
  creates a session dir under TMPDIR and dies with "Read-only file system
  (os error 30): tmpdir" wherever the container's /tmp is not writable.

The module test suite previously passed `checkIfExists` on a directory that
existed but was empty, which is why it never caught any of this. It now has a
stub test and a real-render test with a VEP somatic VCF, so a broken container,
an incomplete tool tree or CLI drift all fail loudly.

Refs #133

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 12, 2026 09:29
The vendored tool tree needs a `linguist-vendored` entry so GitHub does not
count 565 KB of upstream R and SCSS as pipeline source, but .gitattributes is
template-managed and any edit fails files_unchanged. Opt it out the way the
repo already opts out CODE_OF_CONDUCT.md and the workflow files.

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

Copilot AI 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.

Pull request overview

Copilot reviewed 54 out of 57 changed files in this pull request and generated no new comments.

Suppressed comments (3)

modules/local/lrsomaticreport/main.nf:16

  • The PR description says container: points at community.wave.seqera.io/library/r-base_quarto_r-data.table_r-dplyr_pruned:f1d36670d940c971, but this module hard-codes different Wave image tags/digests. Please align the PR description with the actual container references in code (or vice versa) so users know which image is expected to work.
    workflows/lrsomatic.nf:961
  • ASCAT.out.png can emit a list of PNGs for a sample. After .mix(...).groupTuple() that can produce nested lists (e.g. [file, [png1,png2]]), which then gets passed downstream as ascat_files. Flattening here (as you already do for Wakhan) keeps the contract consistently [meta, [file, ...]] and avoids surprising staging behavior in LRSOMATICREPORT.
    workflows/lrsomatic.nf:545
  • The updated comment describes meta.paired_data as a boolean, but elsewhere it’s treated as a truthy/falsy value (it may be an id string for the paired sample). To avoid misleading future changes, reword the comment to describe the truthiness contract rather than a strict boolean.

Copilot AI review requested due to automatic review settings August 12, 2026 09:34

Copilot AI 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.

Pull request overview

Copilot reviewed 54 out of 57 changed files in this pull request and generated no new comments.

Suppressed comments (3)

conf/modules.config:563

  • ext.args passes --gene-panel without quoting the parameter value. If --report_gene_panel is a file path containing spaces (common on shared filesystems) or shell metacharacters, the rendered command line will break or be mis-parsed. Quote the value so it is passed as a single argument.
    modules/local/lrsomaticreport/main.nf:16
  • The PR description states the report module uses a specific Wave image tag (...r-data.table_r-dplyr_pruned:f1d36670d940c971), but the module is pinned to different tags here (...r-base64enc_r-data.table_pruned:dc62d809aa6fd497 / ...:c1049dbaf31bf178). This kind of drift can lead to hard-to-reproduce render failures if the pinned container doesn't match environment.yml. Please align the PR documentation and the pinned image(s), and double-check the pinned image actually contains all R/Quarto deps listed in environment.yml.
    assets/lrsomatic_report/VENDORED.md:10
  • The PR description says assets/lrsomatic_report was added as a git submodule pinned to a specific commit, but this file explicitly documents that the directory is vendored (not a submodule) and records different upstream/vendored SHAs. Please update the PR description to match the actual approach used in the diff so reviewers/users aren't misled about how the report source is managed.
This directory is a **vendored copy** of the standalone report tool, not a git submodule.
Do not edit it here — fix upstream, tag a release, and re-sync.

| | |
|---|---|
| Upstream | <https://github.com/ljwharbers/lrsomatic_report> |
| Release | `v1.1.0` (`9d660a77d5f23f92e1f7ff34f85da7956f445009`) |
| Vendored commit | `d17a636aeb3f79462b7f58db9102f4030941195b` (`main`) |

ljwharbers and others added 2 commits August 14, 2026 14:27
Addresses the two review comments still live on PR #176.

--report_gene_panel is documented as accepting a path to a TSV, but the raw
param was interpolated straight into the command line: the file was never
staged, so it was not bound into the docker/singularity container and
render_report.R aborted with "--gene-panel not found". Add an optional
`gene_panel` path input, wired from the workflow only when the param resolves
to an existing file (a builtin panel name still travels via ext.args alone),
and have conf/modules.config pass the quoted *base* name -- unchanged for a
builtin, and the staged name for a TSV. Quoting also fixes panel paths
containing spaces.

The activation-hook loop hard-coded CONDA_PREFIX=/opt/conda, which is right
for the Wave image but wrong under -profile conda, where it already points at
the task's own env. Only fall back to /opt/conda when unset, and glob the
hooks from $CONDA_PREFIX.

Also pass checkIfExists to the report_src lookup so a bad --report_src fails
fast, and cover the panel path with a real (non-stub) nf-test that renders
with a user-supplied TSV -- it only passes if the file is genuinely staged
into the container.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ljwharbers and others added 2 commits September 1, 2026 13:19
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…iner-and-ascat-list

Fix/lrsomaticreport container and ascat list
Copilot AI review requested due to automatic review settings September 1, 2026 15:06

Copilot AI 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.

Pull request overview

Copilot reviewed 63 out of 66 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

README.md:107

  • Grammar issue: “a multiqc report from that combines …” should be “a multiqc report that combines …”.
This pipeline produces a series of different output files. The main output is an aligned and phased tumour bam file. This bam file can be used by any typical downstream tool that uses bam files as input. Furthermore, we have sample-specific QC outputs from `cramino` (fastq), `cramino` (bam), `mosdepth`, `samtools` (stats/flagstat/idxstats), and optionally `fibertools`. Finally, we have a `multiqc` report from that combines the output from `mosdepth` and `samtools` into one html report, and a self-contained per-sample HTML report (`<sample>/report/<sample>_report.html`) covering small variants, structural variants, copy number and QC in one place — disable it with `--skip_report`.

Comment thread .nf-core.yml
ljwharbers and others added 3 commits September 1, 2026 22:23
The report tool's --gene-panel became repeatable in lrsomatic_report v1.3.0 (upstream
PR #16), so any number of panels can be active in one report and a variant or SV is kept
if it hits any of them. Nothing pipeline-side could deliver that while the vendored copy
was v1.2.1, where is_no_gene_panel() reads a vector of panels as "no panel", so this both
re-syncs the tool and widens --report_gene_panel to a comma-separated list of builtin
names and/or TSV paths. A single value behaves exactly as before.

Only real files are staged, now into gene_panels/ rather than the task root: there are N
of them and their names come from the user, while the root also holds sample_dir/,
versions.yml and the output HTML. Two panel files sharing a base name would collide there
whatever directories they came from, so that combination is rejected up front.

conf/modules.config builds the repeated --gene-panel flags, and does so with string
operations only. `file()` is not in scope inside an ext.args closure -- the delegate is
the config script binding -- and the failure is a task-time MissingMethodException, not a
parse error. The pre-existing `file(params.report_gene_panel).name` had the same defect
and was simply never reached: every pipeline-level test left report_gene_panel null, and
the module tests hard-code ext.args. tests/default.nf.test now passes a builtin and a
panel file, which is what catches it.

So "is this entry a panel file?" is a textual test (a path separator, or a .tsv suffix)
shared by the staging decision and the flag builder, rather than a filesystem probe on
one side and a guess on the other. validateReportGenePanels() then rejects at launch
anything where the textual test and the filesystem would disagree -- along with a
missing panel file, a name that is not a builtin, `none` mixed with a real panel, and
duplicate base names. Previously a typo'd path staged nothing, raised nothing, and killed
the run inside the report task after alignment, calling and annotation had all run.

The vendored re-sync needed no dependency change: a library()/require() grep over
v1.3.0's R/, bin/ and templates/ resolves to packages already pinned in environment.yml,
and upstream recipe/meta.yaml only moved its version string. Both Wave container digests
therefore stand. Version strings carried by hand were bumped together per VENDORED.md:
the module's version topic, meta.yml, the module snapshot and the five pipeline snapshots.

Verified under -profile test,singularity on Mindwell: all four module tests pass,
including a new three-panel real render that asserts DEFAULT_PANELS names every panel --
had only the last --gene-panel survived, it would name one. The rendered report shows
four panel checkboxes with the three requested ones ticked. default, deep_only, consensus
and union pass at pipeline level. clair_only fails on md5s this branch cannot affect
(sample{1,2} normal samtools stats, severus breakpoints_double.csv and read_qual.txt, and
the merged sample4_tumor.bam, whose digest differs between two runs of identical code);
its snapshot is otherwise untouched here apart from the version string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sample4 is the only sample in test_sheet_2.csv with two tumour replicates, so the only one
that goes through SAMTOOLS_MERGE. Both replicate BAMs carry `@PG ID:minimap2` and
`@PG ID:samtools` from identical align/sort commands, and merge disambiguates the collision
with a random hex suffix -- minimap2-6CC32AF6 in one run, minimap2-39EB7FA0 in the next. So
the digest in tests/clair_only.nf.test.snap could never match: not flaky-in-one-environment
but unpinnable in principle.

That suffix is the only difference between runs. Header length is identical at 1712 bytes
and all 186,714,518 bytes of alignment records are byte-identical. The index moves with it
because those bytes change the header's compressed BGZF block size, shifting every virtual
offset -- which is why the .bai has to go too.

Both files are now in tests/.nftignore, and the reads are asserted instead with
bam().getReadsMD5(), the approach modules/nf-core/samtools/merge's own test already takes
for the same reason. That needs nft-bam loaded in nf-test.config; the vendored nf-core
minimap2/align, longphase/haplotag and samtools/merge tests already assume it, but
nf-test.config ignores modules/nf-core/**/tests/* so it was never loaded. stable_name keeps
both filenames -- their existence is still snapshotted, alongside the .exists() assertion.

Regenerating touched only what it should: the two entries dropped, sample4_merged_reads
added, and every other digest reproduced its committed value byte for byte.

This does not make clair_only pass. Germline phasing is nondeterministic -- three runs
produced three distinct phased-VCF digests -- and that propagates into the haplotagged BAMs
(HP tags) and the whatshap stats, none of which are ignored. sample1's BAMs and whatshap
stats matched in three of four runs and not in the fourth. Since the same files are
snapshotted by all five pipeline tests, deciding what to do about that is a wider call than
this commit, which fixes only the entry that failed every single time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…iner-and-ascat-list

Apply several report gene panels at once (lrsomatic_report v1.3.0)
Copilot AI review requested due to automatic review settings September 2, 2026 09:11

Copilot AI 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.

🔵 Needs a closer look

It introduces a substantial new final pipeline stage plus a large vendored tool subtree, so it warrants a final human pass despite only minor identified issues.

Review details

Suppressed comments (1)

README.md:107

  • The sentence has a grammatical error: "a multiqc report from that combines" should be "a multiqc report that combines".
This pipeline produces a series of different output files. The main output is an aligned and phased tumour bam file. This bam file can be used by any typical downstream tool that uses bam files as input. Furthermore, we have sample-specific QC outputs from `cramino` (fastq), `cramino` (bam), `mosdepth`, `samtools` (stats/flagstat/idxstats), and optionally `fibertools`. Finally, we have a `multiqc` report from that combines the output from `mosdepth` and `samtools` into one html report, and a self-contained per-sample HTML report (`<sample>/report/<sample>_report.html`) covering small variants, structural variants, copy number and QC in one place — disable it with `--skip_report`.
  • Files reviewed: 71/74 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread assets/lrsomatic_report/VENDORED.md Outdated
Comment on lines +9 to +10
| Release | `v1.3.0` (`236ab35d5d7c018df1c6b477ecc867c60efd2ff9`) |
| Vendored commit | `236ab35d5d7c018df1c6b477ecc867c60efd2ff9` (the tag itself) |
Two nits from the Copilot review threads on #176 that were still real:

- README: "a `multiqc` report from that combines" dropped the stray "from",
  and "one html report" -> "one HTML report".
- LRSOMATICREPORT: quote the interpolated paths in the script block. The
  `Rscript` invocation passed report_src, the sample id, the sex and the
  output filename unquoted, so a projectDir or sample id containing a space
  would word-split; the flat-input symlink loop had the same shape. ${args}
  stays unquoted -- it is a pre-built argument string that already quotes
  each `--gene-panel` value in conf/modules.config.

No output or version changes, so no snapshots move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix: address remaining PR #176 review comments
Copilot AI review requested due to automatic review settings September 2, 2026 09:32

Copilot AI 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.

🟡 Changes recommended

The new report step currently doesn’t stage WhatsHap stats into the report task (so the Phasing section will always be missing), and there are a couple of robustness/convention issues that should be fixed before merge.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

modules/local/lrsomaticreport/main.nf:16

  • This module’s container selection ignores task.ext.singularity_pull_docker_container, whereas other modules in this repo use it to decide whether to use a Singularity-native image vs a Docker/OCI reference. With -profile singularity plus singularity_pull_docker_container=true, this process would still try to use the oras:// SIF reference, diverging from the rest of the pipeline’s behavior.

Consider matching the established pattern (workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? ... : ...).
subworkflows/local/utils_nfcore_lrsomatic_pipeline/main.nf:289

  • gene_lists_dir.list() can return null (e.g. if params.report_src/assets/gene_lists exists but is not a directory, or on an I/O error). In that case the subsequent .findAll { ... } will throw a NullPointerException during parameter validation, which is a confusing failure mode for a user typo in --report_src.

Guard both isDirectory() and the return value of .list() so validation fails cleanly and predictably.

  • Files reviewed: 71/74 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread workflows/lrsomatic.nf
Comment on lines +1059 to +1065
// Tumor-side QC: keyed by the sample's own id, which for tumor rows is already the report id
ch_mosdepth_summary
.mix(ch_mosdepth_global, ch_cramino_post_txt, ch_bam_stats, ch_bam_flagstat)
.filter { meta, _f -> meta.type == 'tumor' }
.map { meta, f -> [meta.id, f] }
.groupTuple()
.set { report_qc_tumor_ch }
The comments this PR added were far denser than the surrounding code and in
places longer than the lines they described. Cut ~84 lines of commentary
without touching any code:

- lrsomaticreport/main.nf: the 8-line Wave container note, the 7-line input
  block prose, the 7-line gene-panel note and the 4-5 line blocks in the
  script section.
- conf/modules.config: the 16-line block explaining the ext.args closure was
  longer than the closure itself, in a file with no other explanatory comments.
- utils_nfcore_lrsomatic_pipeline: the four new helpers had multi-paragraph
  doc blocks; the file's existing helpers use a one-line header.
- workflows/lrsomatic.nf: the LRSOMATICREPORT section header is now the same
  shape as the other MODULE: headers, and two trailing comments that only
  restated `meta.paired_data` are gone.
- tests/.nftignore, clair_only.nf.test, default.nf.test, module test: the same
  rationale was spelled out at length in two places; one keeps it, the other
  points at it.

Kept the non-obvious reasons: the wave rebuild command, CONDA_PREFIX falling
back only when unset (hard-coding it broke -profile conda), R's list.files()
not descending into symlinked directories, why the panel test in
conf/modules.config has to be textual, and why sample4's merged BAM md5 is
unstable.

Verified: `nextflow config .` resolves the LRSOMATICREPORT block unchanged,
`nextflow lint` clean, pre-commit prettier/whitespace/nf-lint pass, and
`git diff -U0` confirms only comment lines moved. No snapshots affected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 2, 2026 11:04

Copilot AI 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.

🔵 Needs a closer look

Launch-time gene panel validation can throw an NPE when --report_src exists but does not contain a readable assets/gene_lists directory, so the builtin panel discovery needs to be made null-safe.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

subworkflows/local/utils_nfcore_lrsomatic_pipeline/main.nf:281

  • reportBuiltinGenePanels() assumes ${params.report_src}/assets/gene_lists is a directory and calls .list() unguarded. If --report_src points at the wrong existing path (e.g. a file, or a tool tree missing assets/gene_lists/), gene_lists_dir.exists() can still be true but .list() returns null, causing an NPE during parameter validation instead of a clear error / empty builtin list.
  • Files reviewed: 71/74 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Vendored assets/lrsomatic_report is untouched here; its comments are trimmed
upstream in ljwharbers/lrsomatic_report#20 and land with the next re-sync.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 3, 2026 12:56

Copilot AI 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.

🟡 Changes recommended

Parameter validation likely fails at launch because reportBuiltinGenePanels() calls .list() on a file(...) result that is typically a Path, which can throw at runtime.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 71/74 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +266 to +277
def reportBuiltinGenePanels() {
def gene_lists_dir = file("${params.report_src}/assets/gene_lists")
if (!gene_lists_dir.exists()) {
return []
}
return gene_lists_dir
.list()
.findAll { it.endsWith('.tsv') }
.collect { it.replaceFirst(/(\.(hg38|t2t))?\.tsv$/, '') }
.unique()
.sort()
}
Follows assets/lrsomatic_report/VENDORED.md: replaces bin/, R/, templates/, assets/,
LICENSE and README.md from the v1.3.2 tag (75c65b2), updates the VENDORED.md table,
the version topic in the module and meta.yml, the snapshot version lines and the
CHANGELOG. environment.yml is unchanged: the recipe's dependency list still matches,
so the container digests stay.

v1.3.2 brings the v1.3.1 changes (live facet counts, facet menu keeps the table's
horizontal scroll, flatter theme) plus one-line inline comments (lrsomatic_report#20).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 3, 2026 13:06

Copilot AI 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.

🟡 Changes recommended

The report module can fail when linking inputs that include path components (e.g. SEVERUS outputs) and the rendered report footer currently advertises an inconsistent tool version.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 71/74 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +31 to +35
// Discovery is recursive and matches on base name, so suffix-distinct files can be linked flat
def flat_inputs = [vep_somatic, sv_vep, severus_vcf, ascat_files].flatten().findAll { f -> f }
def link_flat = flat_inputs ? """
for f in ${flat_inputs.collect { f -> "\"${f}\"" }.join(' ')}; do ln -s "\$PWD/\$f" "sample_dir/\$f"; done
""" : ''

{{< include sections/_qc.qmd >}}

*Report generated `r format(Sys.time(), "%Y-%m-%d %H:%M")` · LRSomatic report v1.3.1*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants