Skip to content

Make the Nextflow workflows actually run, and execute the raw-reanalysis path for real - #5

Merged
sr320 merged 3 commits into
mainfrom
run-raw-reanalysis-pipeline
Aug 28, 2026
Merged

sr320 merged 3 commits into
mainfrom
run-raw-reanalysis-pipeline

Conversation

@sr320

@sr320 sr320 commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Ran the CALLA2026_OSHV pilot on real hardware against real public FASTQ. It found sixteen defects, and the headline is uncomfortable: the reproducibility layer did not work. Three of the four workflows did not compile on current Nextflow, the fourth silently skipped two thirds of its processes while exiting 0, and aree harmonize --input — one of the six commands in the project brief — read a different file than the one you passed it.

All sixteen are fixed. The RNA-seq raw_reanalysis path now runs end to end.

Why a pilot rather than the full study

One contrast, 11 libraries, first 1M read pairs each (~1.5 GB), streamed from ENA and truncated. Purpose: make the workflow run, not produce biology.

This was the right call. A 226 GB download and a day of compute would have died on line 38 of main.nf, at def VALID_MODES = [...].

The run

38 processes green: FASTQC ×22 → fastp ×11 → Salmon index → Salmon quant ×11 → MultiQC → DESeq2 → standardize → manifest → report. ~7 min on an M4, native tools via -profile local.

Two results worth recording:

  • 22,301 genes, every one with a real lfcSE, 22,276 with an unadjusted p-value. This is exactly what HESSER2024_VCOR cannot supply and the whole reason this study was chosen for raw reanalysis.
  • Identifier resolution is 100.0% exact (4,000-identifier dry run) versus 87.2% for HESSER — confirming the reference-choice prediction recorded in the study YAML. All 33,068 GeneIDs in the reference GTF are in the crosswalk, none unmatched.

The output is not valid biology and has not been harmonized. First-N reads are not a random subsample, and 1M pairs under-powers a DE test. CALLA2026_OSHV still contributes zero evidence records.

The sixteen defects

Would not compile (Nextflow 26.04 strict DSL2): bare script-level statements in rnaseq and proteomics; workflow.onComplete at script level in three workflows; then params and workflow both unresolvable inside the relocated closure, giving an NPE after every otherwise-successful run.

Ran but broken: proteomics RENDER_REPORT copied the template onto itself (cp rejects identical paths); metabolomics set qc_json_for_manifest = Channel.empty().collect(), which emits nothing rather than an empty list — so EMIT_MANIFEST never fired and RENDER_REPORT, depending on it, never fired either. Exit 0, completed=1, no manifest, no report, no error. That is the most dangerous item on the list; every other defect announces itself.

DESeq2 module could not accept a real study: condition levels hardcoded to c("control","treatment") — an all-NA factor for any study not named that way, i.e. every real study; quant_subdir mandatory; tx2gene header assumed; ignoreTxVersion strips versions from quant.sf but not from tx2gene, so any NCBI GTF stops matching; and a bare $ before a quote mangled by Groovy interpolation, corrupting the R regex into sub("\\..*DIFFERENTIAL_EXPRESSION_DESEQ2, "".

The Nextflow → AREE handoff, never actually connected: comparison resolved by filename only, so workflow output matched nothing; --input was ignored — it selected a comparison and then harmonized the registry's results_file instead of the file given. Existing tests missed this because they always passed the declared path, so reading the wrong file produced identical output. It also made raw reanalysis impossible by construction, since a raw-mode study has results_file: null. Plus manifest writing crashed on any path outside the repo, which is where workflow output lives.

Also: SAMPLE_QC declared multiqc_data but MultiQC writes multiqc_report_data, failing the task as a missing output after MultiQC exited 0 and wrote everything.

Environment findings

  • Quarto fails on exFAT. RENDER_REPORT dies in Quarto's cleanup with the work dir on an exFAT volume; identical run succeeds on APFS. Publishing Salmon directories to exFAT also fails. Keep -work-dir and --outdir on APFS.
  • A read glob must not begin with a character class. [SD]*_{1,2}.fastq.gz, used to skip macOS AppleDouble sidecars, silently breaks fromFilePairs key extraction and yields empty sample ids.
  • No container needed on Apple Silicon. brew install nextflow fastp fastqc salmon + BiocManager + pip install multiqc is sufficient, and avoids x86 emulation.

Docs

implementation_status.md, roadmap.md, first_raw_reanalysis.md and all four workflow READMEs are corrected. The status table previously claimed "DSL2 wiring correct; DAG executes in -stub-run mode" — that had become false, and the page now says so explicitly rather than quietly editing it out.

Every page keeps the qualifications prominent: subsampled, one of six comparisons, no container ever pulled, nothing harmonized, and random-effects pooling on real data still unexercised.

Verification

149 Python tests, ruff clean, docs render, and all four workflows run processed_results_harmonization end to end — which none of them did before this branch.

Also removes ~80 Nextflow run artifacts that git add -A swept into the first commit, and gitignores them.

🤖 Generated with Claude Code

sr320 and others added 3 commits August 28, 2026 13:32
…rmonize

Running the pilot for CALLA2026_OSHV surfaced eleven defects. All of them were
hidden behind "scaffolded but not yet production-ready", which was true but
understated: three of the four workflows did not compile on current Nextflow,
and the raw-reanalysis path could not have worked at all.

Workflows would not parse (Nextflow 26.04, strict DSL2):

* bare script-level statements — `def VALID_MODES = ...` in rnaseq,
  `workflow_version = ...` in proteomics — are no longer legal; both are now
  function declarations;
* `workflow.onComplete { }` at script level, in rnaseq/proteomics/methylation,
  moved inside the entry workflow;
* neither `params` nor `workflow` resolves inside a closure nested in the
  workflow body, so both are snapshotted into locals the handler captures.
  Without this every run ended in a NullPointerException after succeeding.

Workflows that ran but were broken:

* proteomics RENDER_REPORT did `cp "report_template.qmd" report_template.qmd`,
  which cp rejects as identical; it now renders in place like rnaseq;
* metabolomics set `qc_json_for_manifest = Channel.empty().collect()`, which
  emits nothing rather than an empty list, so EMIT_MANIFEST never received a
  value and it plus RENDER_REPORT were silently skipped — exit 0, no manifest,
  no report, "completed=1" and no error. Now `Channel.value([])`.

DESeq2 module could not accept a real study:

* condition levels were hardcoded to c("control","treatment"), which would
  produce an all-NA factor for any study whose groups are named anything else —
  i.e. every real study. The two levels are now named via --control_level /
  --treatment_level, with a clear error listing the levels actually present,
  a filter so a whole-BioProject sheet can be subset to one contrast, and a
  replication guard (error below n=2, warning below n=3);
* quant_subdir was mandatory though Salmon names output dirs for the sample;
  it now defaults to sample_id;
* tx2gene was read with header=TRUE unconditionally, silently dropping the
  first transcript of a headerless map.

The Nextflow -> AREE handoff, which had never been connected:

* `aree harmonize --input` resolved the comparison by matching the filename
  against comparisons[].results_file. Workflow output is named for the stage
  that produced it, so it matched nothing. Added --comparison.
* Worse, --input was only ever a selector: once the comparison was found, the
  code harmonized the registry-declared results_file, not the file passed in.
  A documented top-level command did not do what it says. Existing tests missed
  it because they always passed the declared path, so reading the wrong file
  produced identical output. --input now takes precedence, with the registry
  path as fallback — and this is what makes a raw_reanalysis study, whose
  results_file is null, harmonizable at all.
* manifest writing crashed with ValueError on any input outside the repo, which
  is where workflow output lives.

Verified: all four workflows now run processed_results_harmonization end to end
(3/3 processes, SUCCESS), and rnaseq output harmonizes into evidence records
with lfcSE intact. Tests 146 -> 149.

Environment note recorded for the pilot: Quarto's cleanup fails on exFAT, so
the Nextflow work directory needs an APFS path even when read data lives on an
external volume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nd to end

The RNA-seq raw_reanalysis pipeline has now executed against real public FASTQ
for the first time: 38 processes — FASTQC x22, fastp x11, Salmon index, Salmon
quant x11, MultiQC, DESeq2, standardize, manifest, report — completing green on
a subsampled 11-sample contrast from PRJNA1329250.

Fixes needed to get there, on top of the eleven in the previous commit:

* SAMPLE_QC declared `path "multiqc_data"`, but MultiQC names its data
  directory after the report file, so `-n multiqc_report.html` writes
  multiqc_report_data/. The task failed as a missing output while MultiQC had
  exited 0 and written everything correctly.
* tximport(ignoreTxVersion = TRUE) strips the version suffix from the ids read
  out of quant.sf but leaves tx2gene untouched, so a map built from a versioned
  annotation — which is every NCBI GTF — silently stopped matching. Both sides
  are now stripped.
* A bare `$` immediately before a quote inside the process script block is
  mangled by Groovy string interpolation, which corrupted the R regex into
  `sub("\\..*DIFFERENTIAL_EXPRESSION_DESEQ2, ""`. The anchor is unnecessary;
  dropped it rather than fighting the escaping.
* The startup banner read only params.rnaseq.comparison_id, so a
  --comparison_id given on the command line printed as null even though the run
  used it. Falls back to the flat param now.
* The read glob must not begin with a character class. `[SD]*_{1,2}.fastq.gz`,
  used to skip the macOS AppleDouble sidecars an exFAT volume creates, silently
  broke fromFilePairs key extraction and produced empty sample ids that would
  match nothing in the sample sheet. Anchored on the run-accession prefix
  instead, with a comment saying why.

Removes ~80 Nextflow run artifacts (dag/report/timeline/trace html and txt)
that `git add -A` swept into the previous commit, and gitignores them.

Result of the run, which is a pipeline test and NOT valid biology — the reads
are the first 1M pairs per library, not a random subsample, and nothing from it
has been harmonized into the evidence table:

* 22,301 genes quantified, every one with a real lfcSE and 22,276 with a raw
  p-value. That is the property HESSER2024_VCOR lacks and the whole reason this
  study was selected for raw reanalysis.
* Identifier resolution against the real crosswalk is 100.0% exact on a
  4,000-identifier dry run, versus 87.2% for HESSER2024_VCOR. This confirms the
  prediction recorded in the study YAML: quantifying against the same
  annotation the crosswalk is built from removes the assembly crossing.

Verified after the changes: 149 Python tests pass, ruff clean, and all four
workflows still run processed_results_harmonization end to end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The status pages described the Nextflow layer as reviewed-but-unexecuted
scaffold. That is no longer accurate in either direction: three of the four
workflows did not even compile on current Nextflow, and the RNA-seq one has now
run the full raw_reanalysis path against real public FASTQ.

* implementation_status.md: rewrites the scaffold table. Records that
  processed_results_harmonization is now verified for all four workflows, that
  it was not runnable before today, and that the previous claim of "DSL2 wiring
  correct; DAG executes in -stub-run mode" had become false. A status page that
  quietly corrects itself is worth less than one that says what it got wrong.
* first_raw_reanalysis.md: adds the pilot write-up — the 38-process run, the
  22,301 genes with real lfcSE, the 100.0% exact identifier resolution that
  confirms the reference-choice prediction made at registration, the full table
  of sixteen defects, and the environment notes (Quarto fails on exFAT; a read
  glob must not begin with a character class). Replaces the "Running it"
  section, which still said none of it had been executed and gave a
  -profile docker invocation that has never been tried, with the commands
  actually used.
* the four workflow READMEs and roadmap.md: same correction, scoped to each.

Every page keeps the qualifications prominent: the run was subsampled and
covers one of six comparisons, no container has ever been pulled, nothing has
been harmonized, CALLA2026_OSHV still contributes zero evidence records, and
random-effects pooling on real data remains unexercised.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sr320
sr320 merged commit 362950a into main Aug 28, 2026
4 checks passed
@sr320
sr320 deleted the run-raw-reanalysis-pipeline branch August 28, 2026 21:22
@sr320
sr320 restored the run-raw-reanalysis-pipeline branch August 31, 2026 16:48
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.

1 participant