Skip to content

Single-cell: interactive master summary, TCRdist3 + TCRi fixes, GIANA clonotype dedup - #99

Open
zaidissa wants to merge 6 commits into
v2from
singlecell-master-summary
Open

Single-cell: interactive master summary, TCRdist3 + TCRi fixes, GIANA clonotype dedup#99
zaidissa wants to merge 6 commits into
v2from
singlecell-master-summary

Conversation

@zaidissa

@zaidissa zaidissa commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Single-cell modality work. Both routes verified end to end on 8 samples / 2 patients:
VDJ-only 123 tasks, full SC 126 tasks, 0 failures, 12/12 modules contributing.

Fixes

TCRdist3 was silently dropped from every full-SC run. enrich_seurat.R required
rhdf5, which is absent from the single-cell container (only hdf5r is present), so every
sample fell through to a .csv branch that never exists in sparse mode. Because
tcrdist_export is optional: true, nothing failed — the method just vanished.
Now reads via whichever HDF5 package is available: 0 → 22,496 cells annotated (27%).

TCRi was dead code. modules/scratch/TCRI/ and subworkflows/scratch/tcri.nf existed
but nothing included them, while run_tcri = true in the config implied otherwise. Wired
TCRI_SW into the SC workflow, added the missing tables/figures emits, and guarded an
unbound LD_LIBRARY_PATH that aborted the task under set -u before any R ran.
17,843 / 18,610 cells scored (95.9%).

Master Summary rebuilt. 21 of the 30 file params the .qmd declared were never passed,
so 7 sections rendered blank; stageAs was also stripping file extensions, so no figures
embedded at all. Replaced the ~30 hand-wired params with per-module table staging
(intables/<module>/*), so new upstream tables no longer need process-input changes.
Now 17 interactive plotly figures and 12 sortable tables. Figures use native plot_ly()
rather than ggplotly(), which is broken against ggplot2 4.x in this container.

New CLUSTER_ROLLUP step. GIANA/GLIPH2/TCRdist3 produced raw per-patient output but no
rollup, so those modules always reported absent. Adds rollups plus method coverage, and
radius-based TCRdist clustering for the VDJ-only route.

VDJ_QC: added pairing_bar_by_sample, which the Master Summary requested by name but
no module produced.

Please review: one change reaches the bulk engine

modules/local/compare/giana.nf — collapses to one row per clonotype before GIANA.
PATIENT_CONCATENATE pools a patient's samples by stacking rows, so GIANA was clustering
cross-sample duplicates rather than similar sequences: 76 of 77 clusters on the
single-cell test set contained a single distinct CDR3b
, and single-sample patients
produced no output at all.

This affects bulk too, so it is gated behind giana_dedup_clonotypes, default false
bulk results are unchanged unless explicitly enabled. params_singlecell.yml sets it true.
Happy to drop the change entirely if you would rather handle it separately.

No other bulk-side file is modified, and nothing is deleted.

Not covered

  • Bulk mode has not been executed against this branch (-preview only). The gate makes it a
    no-op by default, but it is unverified.
  • No regression tests added. The rhdf5 issue in particular is invisible when it breaks.
  • Tested on one dataset only.

@zaidissa zaidissa changed the title Single-cell: interactive master summary, TCRdist3 + TCRi fixes, GIANA clonotype dedup ... Single-cell: interactive master summary, TCRdist3 + TCRi fixes, GIANA clonotype dedup Aug 31, 2026
@zaidissa
zaidissa force-pushed the singlecell-master-summary branch from 117b8d9 to 317e4c7 Compare August 31, 2026 16:32
… clonotype dedup

Single-cell modality work. Both routes verified end to end on 8 samples / 2 patients:
VDJ-only 123 tasks, full SC 126 tasks, 0 failures, 12/12 modules contributing.

## Fixes

**TCRdist3 was silently dropped from every full-SC run.** `enrich_seurat.R` required
`rhdf5`, which is absent from the single-cell container (only `hdf5r` is present), so every
sample fell through to a `.csv` branch that never exists in sparse mode. Because
`tcrdist_export` is `optional: true`, nothing failed — the method just vanished.
Now reads via whichever HDF5 package is available: **0 → 22,496 cells annotated (27%)**.

**TCRi was dead code.** `modules/scratch/TCRI/` and `subworkflows/scratch/tcri.nf` existed
but nothing included them, while `run_tcri = true` in the config implied otherwise. Wired
`TCRI_SW` into the SC workflow, added the missing `tables`/`figures` emits, and guarded an
unbound `LD_LIBRARY_PATH` that aborted the task under `set -u` before any R ran.
**17,843 / 18,610 cells scored (95.9%).**

**Master Summary rebuilt.** 21 of the 30 file params the `.qmd` declared were never passed,
so 7 sections rendered blank; `stageAs` was also stripping file extensions, so no figures
embedded at all. Replaced the ~30 hand-wired params with per-module table staging
(`intables/<module>/*`), so new upstream tables no longer need process-input changes.
Now 17 interactive plotly figures and 12 sortable tables. Figures use native `plot_ly()`
rather than `ggplotly()`, which is broken against ggplot2 4.x in this container.

**New `CLUSTER_ROLLUP` step.** GIANA/GLIPH2/TCRdist3 produced raw per-patient output but no
rollup, so those modules always reported absent. Adds rollups plus method coverage, and
radius-based TCRdist clustering for the VDJ-only route.

**`VDJ_QC`**: added `pairing_bar_by_sample`, which the Master Summary requested by name but
no module produced.

## Please review: one change reaches the bulk engine

`modules/local/compare/giana.nf` — collapses to one row per clonotype before GIANA.
`PATIENT_CONCATENATE` pools a patient's samples by stacking rows, so GIANA was clustering
cross-sample duplicates rather than similar sequences: **76 of 77 clusters on the
single-cell test set contained a single distinct CDR3b**, and single-sample patients
produced no output at all.

This affects bulk too, so it is **gated behind `giana_dedup_clonotypes`, default `false`** —
bulk results are unchanged unless explicitly enabled. `params_singlecell.yml` sets it true.
Happy to drop the change entirely if you would rather handle it separately.

No other bulk-side file is modified, and nothing is deleted.

## Not covered

- Bulk mode has not been executed against this branch (`-preview` only). The gate makes it a
  no-op by default, but it is unverified.
- No regression tests added. The `rhdf5` issue in particular is invisible when it breaks.
- Tested on one dataset only.
@zaidissa
zaidissa force-pushed the singlecell-master-summary branch from 317e4c7 to c50f985 Compare August 31, 2026 16:32
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Unit Test Results

17 tests  ±0   17 ✅ ±0   8m 19s ⏱️ + 1m 0s
 7 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit e9c75dc. ± Comparison against base commit 60726e4.

♻️ This comment has been updated with latest results.

- process-form.json / process-input.json: add patient_col, giana_dedup_clonotypes
  and tcrdist_radius to both single-cell profiles; run_tcri on full_gex only
  (it needs a GEX object).
- preprocess.py: warn when the sample sheet has no patient column. PATIENT pools
  samples by it for GIANA/GLIPH2, so without it every sample becomes its own
  patient and the clustering is silently wrong. Also logs the resolved
  sample/patient counts. The check is defensive and cannot fail preprocessing.
The VDJ-only route emits no per-cell object. BULK_TO_EXPORT synthesizes a
clonotype-level export whose cells are reconstructed rather than real barcodes
(4,372 synthetic rows against 23,764 actual cells on the 8-sample test set).

Builds a merged object from VDJ_QC's contig tables at both stages, keeping real
barcodes: a Seurat object with 27 TCR metadata columns, the scRepertoire
combineTCR object, and flat per-cell and per-sample tables. The pre/post pair
makes QC's effect directly measurable.

cellranger vdj has no genes, so the Seurat counts assay is a zero placeholder;
this is recorded in seu@misc$provenance. Runs on both routes, toggleable via
run_merge_vdj_object.

Also narrows the run-artifact ignore rule from work*/ to work/ and work_*/ -
the wildcard form also matched workflows/, the source directory.

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

This PR improves the single-cell (SC) pipeline route by restoring previously missing analyses (notably TCRdist3 and TCRi), rebuilding the Master Summary report input model to be module-table–driven, and adding new “bridge” rollups/artifacts to make SC outputs complete and comparable across routes. It also introduces an optional GIANA clonotype deduplication step (default off) that can affect bulk behavior only when explicitly enabled.

Changes:

  • Wire TCRI_SW into the full-SC workflow, fix LD_LIBRARY_PATH under set -u, and re-export TCRi tables/figures for downstream reporting.
  • Rework Master Summary inputs from ~30 individually-wired params to per-module staged tables/ directories (and update the QMD accordingly), plus add new cluster rollup staging for GIANA/GLIPH2/TCRdist3.
  • Add optional GIANA per-patient deduplication (giana_dedup_clonotypes) to prevent cross-sample duplicate rows inflating “clusters”; enable it for single-cell via params_singlecell.yml.

Reviewed changes

Copilot reviewed 18 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
workflows/tcrtoolkit_sc.nf Wires TCRi into SC workflow; stages per-module tables into Master Summary; adds cluster rollups + merged VDJ object step.
subworkflows/scratch/tcri.nf Re-exports TCRi tables/figures for consumption by the Master Summary workflow.
subworkflows/scratch/master_summary.nf Updates Master Summary subworkflow interface to accept per-module table collections.
params_singlecell.yml Enables giana_dedup_clonotypes for SC runs.
nextflow.config Adds giana_dedup_clonotypes, run_merge_vdj_object, and tcri_scores_file params.
modules/scratch/VDJ_QC/VDJ_QC_analysis.qmd Adds pairing-by-sample bar plot that the report expects.
modules/scratch/TCRI/main.nf Fixes LD_LIBRARY_PATH expansion under set -u.
modules/scratch/MASTER_SUMMARY/Master_Summary_Report.qmd Major rebuild: module-table directory model + interactive Plotly/Reactable report rendering.
modules/scratch/MASTER_SUMMARY/main.nf Stages per-module tables/ directories under intables/<module>/ and updates Quarto params.
modules/local/compare/giana.nf Adds optional clonotype-row deduplication prior to GIANA (gated by giana_dedup_clonotypes).
modules/bridges/merge_vdj_object.nf New bridge process to build pre/post-QC per-cell VDJ object outputs.
modules/bridges/cluster_rollup.nf New bridge process to generate rollup tables for cluster methods and method coverage.
bin/merge_vdj_object.R New script to build combineTCR + placeholder Seurat + per-cell TSV from contigs.
bin/enrich_seurat.R Fixes HDF5 reading by supporting hdf5r when rhdf5 is unavailable.
bin/cluster_rollup.py New script to generate GIANA/GLIPH2/TCRdist3 rollups and method coverage tables.
.gitignore Ignores additional run artifact directories; un-ignores notebooks/template_*.qmd.
.cirro/singlecell_vdjonly/process-input.json Adds patient_col, GIANA dedup toggle, and tcrdist_radius to Cirro wiring.
.cirro/singlecell_vdjonly/process-form.json Adds UI fields for patient_col, GIANA dedup toggle, and tcrdist_radius.
.cirro/singlecell_vdjonly/preprocess.py Adds preprocessing warnings/info around missing/NA patient column.
.cirro/singlecell_full_gex/process-input.json Adds patient_col, run_tcri, GIANA dedup toggle, and tcrdist_radius to Cirro wiring.
.cirro/singlecell_full_gex/process-form.json Adds UI fields for patient_col, run_tcri, GIANA dedup toggle, and tcrdist_radius.
.cirro/singlecell_full_gex/preprocess.py Adds preprocessing warnings/info around missing/NA patient column.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread modules/local/compare/giana.nf Outdated
dltamayo and others added 3 commits September 1, 2026 15:31
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The Copilot Autofix commit (2bea78f) that changed the dedup_clonotypes
truthiness check from ?: to == true also dropped the script: label
above it. Without it, Nextflow's parser stays in output-declaration
mode and treats the def statement as an output entry, failing with
"Invalid process output" at giana.nf:16 - this broke script
compilation for every bulk test touching patient/compare (16 of 17
integration tests failed in PR #99's CI run).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The three clustering modules write raw per-patient output and no figures - the bulk
notebooks that plot them (template_giana.qmd, template_gliph.qmd) render only in bulk
mode, so on the single-cell route GIANA and GLIPH2 computed correctly but nothing
displayed them.

CLUSTER_ROLLUP now emits five detail tables read straight from that raw output:
per-cluster membership for each method, and V-gene usage among clustered clonotypes.
Each cluster row carries n_members, n_unique_cdr3 and n_samples, so a cluster whose
members are all one CDR3b is visibly duplicate detection rather than similarity.

Master Summary gains four figures and a table: cluster size distribution across
methods, top GLIPH2 motif clusters, the fraction of clusters spanning more than one
sample, the GIANA cluster table, and V-gene usage. 13 -> 17 interactive figures.

On the 8-sample test set: 89/89 GLIPH2 motifs and 7/7 GIANA clusters contain more than
one distinct CDR3b, and roughly three quarters of clusters from both methods span
multiple samples.
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.

3 participants