Exports and batch correction:
Figure downloads (every figure can be saved as PNG, JPG and PDF):
Results Summary:
Workspaces:
Gene ID conversion (Step 1 / STEP 2b) now works for any microarray platform:
download_volcano_png.htm ("Site wasn't available"). Two label assignments sat inside
the ggplot + chain (R error: could not find function "+<-"). New test guards against
assignments inside + chains anywhere in the package.invalid connection error from the one-line installer (network blocks
raw.githubusercontent.com) and points to the manual install; troubleshooting row for the
RStudio "Rtools is required" warning.shiny::runGitHub() chunk sets options(timeout = 600) so the GitHub download
is not cut off after R's 60 s default on slow connections ("incomplete block on file").wininet download method.gexpipe_setup() now start the app with
shiny::runApp(app) instead of a fixed port = 3838L, so the free port chosen by
runGExPipe() is used. Fixes "createTcpServer: address already in use" when port 3838 is busy.install_gexpipe.R) and the vignette manual install now also install the optional
packages (Boruta, car, cicerone, corrplot, dcurves, kernlab, mixOmics, SHAPforxgboost), so
every app feature works after one install.00LOCK folders and busy ports; the Windows fix now
also removes a plain 00LOCK folder.Example GSE IDs: training examples are RNA-seq GSE144119, GSE100026 and microarray GSE268456, GSE47927; validation examples are RNA-seq GSE162462 and microarray GSE13159.
Batch Correction page, Parallel mode: the expression export now gives a separate before and after CSV for RNA-seq and for microarray (four files), matching the per-platform batch correction. Single/Merged mode keeps the two combined files.
Step 3 Quality Control, Parallel mode: the Venn and UpSet plots no longer pool all datasets. Common genes are now identified separately for the RNA-seq datasets and for the microarray datasets (own Venn, UpSet, common-gene count and PNG/JPG/PDF downloads for each platform).
Step 7 (Consensus): two new figures with 300 DPI PNG/JPG/PDF downloads, also auto-saved on Apply: DEG_LogFC_Concordance_Scatter.png (RNA-seq vs microarray logFC of the common DEGs, % same direction, Spearman rho, top genes labelled) and Consensus_DEG_Hierarchical_Heatmap.png (top consensus DEGs, z-scored within platform, clustered, annotated by Condition and Platform).
Step 3 Quality Control, Parallel mode: Sample Outlier Detection now shows its graphs. PCA (Mahalanobis) and sample-connectivity plots appear for RNA-seq and for microarray (PNG, JPG 300 DPI and PDF downloads), and the "Exclude Selected Outliers" selector is available. Previously these were only drawn in Merged mode.
Gene-expression boxplots (training and validation): Disease is now red and Normal green.
ROC step (Step 12): per-gene AUC tables now include DeLong 95% confidence intervals (AUC_Lower_95CI / AUC_Upper_95CI) for training, and for validation in the Training vs Validation comparison table; the CIs are also in the CSV downloads.
Results Summary: new "Where did my genes go?" gene funnel. It lists the number of genes left after each step (Step 6 DE, Step 7 consensus, Step 8 common genes, Step 10 ML, Step 12 AUC >= 0.8, nomogram) and names the first step that reached zero, with a hint on which cutoff to relax.
Second filter (Groups step and External Validation): empty values in the filter column are now a tickable "(blank)" choice, ticked by default. Previously only non-empty values were listed, so samples with an empty value (e.g. controls in a "disease stage" column that only exists for patients, as in GSE47927) could never be kept and were silently dropped, leaving only one group. The filter panel also warns in red when a filter removes every sample of a group, and the validation run stops with the same hint.
Validation (Step 12): new "Signature validation" panel. Single-gene significance and fold-change cutoffs do not transfer between platforms or tissues, so the whole signature is scored instead: each validation sample gets the mean of the signed, standardized expression of the signature genes (direction learned on the training datasets by a per-dataset Stouffer meta-analysis, so platforms and batches never mix). Reports the AUC with a DeLong 95% CI, permutation p-values against random genes and against random genes that passed the same training filter (the second catches winner's-curse selection), per-gene direction replication, the AUC for several signature sizes, and a leave-one-dataset-out table and figure across the training datasets. Gene source: meta-analysis top N (default), all passing genes, Step 8/10/12 genes, or a custom list. Every run is appended to a "validation attempts log" so all validation sets tried can be reported. New helpers in R/gexp_signature_validation.R and R/server_signature_validation.R.
ROC step (Step 12): the training and validation gene-expression panels are now built by one shared function, so they have identical gene order (training-AUC order, genes present in both), group order (Normal, Disease), colours, layout and size. Each panel shows a bracket with significance stars (Wilcoxon rank-sum per gene, Benjamini-Hochberg adjusted across the genes shown) and the group sizes. Previously the validation panel used the ML gene order, and group colours depended on which group appeared first in the data.
Batch Correction and DE pages: removed the duplicate "Next" buttons. Each page now has a single Next button at the bottom (after the logs); the extra one directly under "Apply Batch Correction" / "Run DE Analysis" and its unused handlers are gone.
Groups (Step 4): the optional second filter now shows its own result ("After this filter: CML = 48, Healthy = 17 (65 of 97 samples kept)"); the Column Preview below it counts before the filter, which was easy to misread.
Diagnostic model + ROC step: fixes for misleading external-validation results.
direction = "auto" separately in training
and validation, so a gene that goes up in training but down in validation still
looked validated. The direction is now fixed from the training data; a reversed
gene shows validation AUC < 0.5 and Direction_Replicated = FALSE
(Direction_Training is added to the tables).Diagnostic model (Step 14, Nomogram): reporting and validation overhaul.
PPI: the Run handler no longer crashes with "argument is of length zero" when the score-threshold / top-hubs inputs are missing (e.g. the settings panel has not rendered); it falls back to the defaults (400 / 15).
Downloads: a GSE already on disk is now always checked FIRST and used, in Training and External Validation, for microarray and RNA-seq; a download only starts when nothing usable is found. Verified offline (network cut off):
rna_data/ext_val_rna (not
only in a per-GSE subfolder) is found; previously it was ignored and the
app spent ~50 s on fallback attempts. The cache check no longer depends on
a validation-only option.Batch correction (Parallel): added a "Same method for both platforms" mode next to Auto (best method per platform) and Manual (pick each platform). RNA-seq and microarray are still corrected separately, just with one chosen method (ComBat-ref, limma, ComBat or SVA).
Groups (Step 4) and External Validation: added a manual group-assignment mode. Choose "Manual: tick samples in the phenodata table", tick rows (column search boxes narrow the table; "Tick all filtered rows" selects every match), then Assign ticked -> Normal / Disease (or Unassign / Clear all). An "Assigned" column shows each sample's group; unassigned samples are excluded. The default column-based method is unchanged.
Downloads: fixed validation (and Groups) phenodata fetch ignoring an
already-downloaded series matrix. gexp_fetch_full_pdata() called GEOquery
with no cache folder, so it re-downloaded the whole GSE (886 MB for
GSE13159) into a temp dir - over an hour. It now reads just the header of
the local *_series_matrix.txt.gz (1.8 s for 2096 samples); the header
reader also stops at the expression table instead of reading the whole
file, and .gexpipe_getgeo_series() reuses a complete local copy when no
cache dir is passed.
Groups (Step 4): same optional second sample filter per dataset ("+ Add second filter"), applied at Extract Groups so skipped samples get no group.
External Validation: added an optional second sample filter next to the group-column selector ("+ Add second filter"). Pick another phenodata column (e.g. tissue/source) and untick values to skip, so the validation cohort can be narrowed (e.g. CML + peripheral blood only). Hidden and off by default; "Remove filter" restores the unfiltered behaviour.
Downloads (Training + External Validation): a manually-downloaded or previously-downloaded GSE is now reliably reused instead of re-downloaded, in both steps:
ext_val_rna/ext_val_micro cache folder on every click, which
forced a full re-download even when the exact same GSE had already been
downloaded (or manually placed there). Now only files for GSE IDs NOT in
the current request are cleared; a file matching a currently-requested
GSE ID is kept and reused.gexp_download_one_microarray_gse/gexp_download_one_rnaseq_gse (the
shared functions used by both Training and Validation) now search
several candidate cache locations for a complete, already-downloaded
file for the requested GSE - including a package-bundled
inst/shinyapp/ext_val_micro/ext_val_rna location - before attempting
any download, and skip straight to using it when found.ext_val_micro/ext_val_rna store, so it stays
available for reuse in later sessions, not just the current one.Downloads: fixed the resumable large-series pre-fetch throwing away real progress on every failed attempt. It used to delete the partial file once it ran out of retries (to stop GEOquery crashing on an incomplete leftover), which meant a user who reached, say, 50% before hitting the retry limit had to start over from 0% on every subsequent try, never actually finishing a large series like GSE13159 in practice. The partial file is now KEPT (it's still valid gzip, just incomplete), and the app stops with a clear message instead of handing it to GEOquery: "download in progress, not yet complete - 50% (443 of 886 MB). Progress is saved; click Download again to continue from here (not from the start)." Clicking Download again resumes from the saved byte offset rather than restarting.
Downloads: added real download-progress reporting for the resumable
large-series pre-fetch (e.g. GSE13159) - "GSEnnnnn: 47% (140 of 300
MB)", updated every ~20 seconds (was: no progress at all, just a spinner
for however long the download took). The total file size comes from a
real HTTP HEAD-style request (Content-Length header), verified to
match the final downloaded size exactly. Reported via
incProgress(amount = 0, detail = ...), which only updates the
displayed text and never moves the bar's numeric value, so it can't
conflict with the outer per-dataset progress bar that already owns
that scale; this also means it's a safe no-op outside a Shiny session
(the manual pipeline scripts), never an error.
Manual pipeline scripts (inst/scripts/manual_pipeline_rnaseq.R,
manual_pipeline_microarray.R): fixed a real gap - both single-GSE
download convenience functions default to fast = TRUE, which skips
gene-ID-to-symbol conversion entirely, and both scripts were calling
them without overriding it. Row names stayed as raw Entrez IDs (RNA-seq)
or raw probe IDs (microarray) all the way through the pipeline, which
every later step (WGCNA, GO/KEGG, STRINGdb) silently mishandled since
they all assume real gene symbols. Confirmed directly with real
downloads before and after: RNA-seq now converts correctly with
fast = FALSE alone; microarray needed an added explicit finishing
step (org.Hs.eg.db against whatever intermediate ID format the
platform's own conversion produces - probe ID, RefSeq, or Ensembl
transcript, which vary by platform), since fast = FALSE alone was
not sufficient there. Verified: 93% of probes (44,053 of 47,323)
resolved to real symbols on a platform that previously returned every
row as an unconverted probe ID.
Also added a guardrail: if group_col/normal_values/disease_values
don't match a dataset's real phenodata, every sample's Condition ends
up NA and gets filtered out - which silently strips colnames() from
the expression matrix entirely (confirmed: matrix[, logical(0)] loses
its colnames even with drop = FALSE), surfacing several steps later
as a confusing "Expression matrix must have colnames" error in Batch
correction instead of a clear one where the real problem is.
Downloads: fixed a crash ("argument is of length zero") introduced by the resumable pre-fetch below when it exhausts every retry attempt without completing - it left the large partial file sitting in GEOquery's cache folder, and GEOquery's own cache-reuse check only looks at whether the file exists, not whether it's complete, so it logged "Using locally cached version" and tried to parse the truncated gzip, crashing deep in its own parser. Reproduced directly against a real partial GSE13159 download to confirm the exact mechanism before fixing it. The partial file is now deleted if every retry attempt fails, so GEOquery always either finds a genuinely complete file or starts completely fresh.
Downloads: very large series (e.g. GSE13159, ~2000+ samples, a
multi-hundred-MB series matrix) now get a resumable, retried pre-fetch
before GEOquery's own download runs. GEOquery's download is a single
unresumed request; on a real run against GSE13159 we found the
connection can be cut short partway through - sometimes as a clear
timeout, but sometimes NCBI's server closes it early with no error at
all, so a plain "did the request succeed" check isn't trustworthy for
this file. The new pre-fetch uses curl::multi_download(resume = TRUE)
(verified directly: a genuine HTTP 206 Range-resume, continuing from
the exact byte a prior attempt stopped at, not restarting), retried up
to 10 times, each attempt continuing from wherever the last stopped.
Completeness is verified by checking the file actually ends with
!series_matrix_table_end, not just "no error was raised" - the same
check already used to detect a truncated cache from an earlier
session. Deliberately conservative: only the plain
"
Gene ID conversion: added automatic detection and conversion of circRNA
IDs using circBase's own numbering (hsa_circ_XXXXXXX) to their host
gene symbol, via a bundled, verified lookup table (4,189 circRNAs, from
circbase.org's Salzman2013/Jeck2013/Memczak2013/Rybak2015/Zhang2013
annotation files). This is a real, safe conversion - the table was
downloaded and checked directly before bundling.
A different, common case - a study's ORIGINAL discovery-cohort ID
(e.g. Arraystar circRNA microarray IDs like "A-NT2RP7011570", which
encode the tissue/cell line a circRNA was first found in) - has no
public, generically-fetchable mapping to a gene symbol (verified: the
exact IDs from GSE71008 do not appear anywhere in circBase's own
downloadable tables). These are now correctly labeled "circRNA
discovery ID - no public gene-symbol mapping available" in the
download log instead of the previous, misleading generic "Microarray
probe-like ID" guess, and are still safely kept as original IDs
rather than converted to a wrong or guessed symbol.
Step 7 (RNA-seq ∩ microarray): added a "Combination method" choice -
Intersection (Consensus, the existing default and recommended for a
final panel: a gene must be significant on both platforms) or Union
(Exploratory, new: a gene significant on either platform passes, with
no cross-platform confirmation). gexpipe_consensus_degs() gained a
combine = c("intersection", "union") argument; the same-direction
filter still only ever applies to genes found on both platforms.
Nomogram: fixed Sensitivity/Specificity and PPV/NPV being swapped in the
performance table (caret::confusionMatrix() treated "Normal" as the
positive class); Disease is now the positive class.
Nomogram: the gene panel now comes from the user's confirmed Step 12 selection (a stale selection from an earlier run is ignored). When the events-per-variable rule forces a reduction, genes are ranked by association with the outcome instead of variance, and the user is told which genes were kept or dropped. Highly correlated genes (|r| > 0.8) are pruned before fitting.
Nomogram: predictors are z-scored per cohort before fitting/prediction, and the external-validation intercept is recalibrated (offset model, slopes kept from training) so training and validation prevalence differences no longer collapse calibration/DCA/clinical-impact curves.
Nomogram: near-separated models (slope above 5 per SD, or SE above 3)
are refit with a ridge penalty; Firth (logistf) coefficients are
reported alongside the standard ones (logistf added to Imports).
Nomogram: added a bootstrap optimism-corrected C-index table, a warning for small validation cohorts, a note on minimum sample sizes, and a message naming the genes responsible when validation predictions are constant (AUC exactly 0.5). The Training calibration plot and both DCA plots now carry titles like the ROC and clinical-impact panels, in both merged and parallel modes.
Validation (Step 11): the DE method chosen by the user (limma, DESeq2, edgeR) is now honoured; it was previously hard-coded to limma. RNA-seq counts are rounded after duplicate-symbol averaging so DESeq2/edgeR stay eligible. GSE examples added to the input box.
ROC (Step 12): the Training vs Validation AUC comparison now uses each gene's true training AUC instead of dropping genes below the AUC cut-off.
PPI (Step 9): fixed an "argument is of length zero" crash caused by an
unrendered gene-set selector returning character(0).
Downloads: the GEO download timeout is raised to 1 hour for very large
series (e.g. GSE13159). Truncated cached series-matrix files (from
interrupted downloads) are now detected by checking for the closing
!series_matrix_table_end line and re-downloaded instead of reused.
Parallel mode: added PNG/JPG/PDF download buttons to all RNA-seq and microarray plots in QC, Normalization, Batch correction and Results.
Removed the misleading "PDF" badge from the "16. Results Summary" menu item (the page has no whole-report PDF download).
Parallel mode (Step 6 Results): added the top-N DE genes heatmap for each platform (RNA-seq and Microarray), with PNG/JPG/PDF downloads. The "Heatmap Genes:" input already existed per platform but had no heatmap wired to it; merged mode already had this heatmap.
Nomogram: fixed a parallel-mode-only data integrity gap - training data in parallel mode is a UNION of RNA-seq and Microarray genes with NA filled in for the platform that never measured a given gene; a gene measured on only one platform was silently fit as a real predictor, causing every sample from the OTHER platform to be dropped from the model without warning. The model now only uses genes present on both platforms, and names any gene excluded for this reason. Merged mode was never affected (its matrices are intersected before training).
Nomogram: added VIF-based collinearity pruning after the pairwise correlation check. Two genes can each have |r| < 0.8 individually while one is a near-linear combination of several others (multi-way collinearity), which pairwise correlation cannot see but shows up as a high VIF; such a gene's coefficient/SE cannot be trusted. The worst offender (VIF > 10) is dropped and the model refit, repeated until every remaining gene's VIF is acceptable, with the reason shown.
Nomogram: fixed misleading guidance in the Model Diagnostics note when quasi-complete separation triggers the ridge-penalized refit - in that case, Coefficient/Std_Error/OR are already the ridge-corrected values, while the _Firth columns come from a separate, unpenalized fit and can look MORE extreme. The note used to always say "trust Firth when they disagree," which was backwards in this specific case; it now explains which columns are already corrected depending on whether ridge fired.
Common Genes (Step 9): fixed a significant parallel-mode-only bug -
right after Step 6's parallel DE finishes, rv$sig_genes is always
overwritten with ONLY the RNA-seq significant genes (a leftover
"default view" assignment unrelated to any user action), so every
Common Genes computation (DEG intersect WGCNA module) silently
excluded all Microarray DEGs from the candidate pool feeding ML, ROC
and the Nomogram - in every parallel-mode run, not just some. Common
Genes now unions both platforms' own significant-gene tables when
Step 7 (consensus) hasn't been run, and still correctly uses Step 7's
real combined result when it has. Merged mode was never affected.
Every step's processing log now uses one shared dark-terminal style with a consistent closing summary block.
combined_expr/expr_micro/expr_rna/unified_metadata to the kept
samples but left combined_expr_before_global_norm untouched, so any
time Step 4 excluded a sample, Parallel-mode Step 5 batch correction
crashed the app with a metadata-alignment error.edgeR::filterByExpr() (built for raw integer counts) being
applied to already-normalized continuous log-intensity data in the
limma DE path, which could silently filter out every gene
("Independent filtering removed all genes"). Independent filtering now
detects whether the input looks like counts and uses an appropriate
filter either way..gexpipe_factor_meta() hardcoding levels = c("Normal", "Disease") when re-leveling Condition: after renaming groups in
Step 4 (e.g. to custom labels), every sample's Condition silently
became NA, destroying the DE design matrix. Group labels are now
preserved regardless of what they're named.Biobase::annotation()
returning character(0)) silently breaking probe-to-symbol GPL
lookups; added a platform_id phenodata fallback..gz file was opened.raw_counts_for_deseq2/rna_counts_list from an earlier,
unrelated download being treated as "available" for a later run with
different samples; now requires real sample overlap with the current
dataset before being trusted.inst/scripts/
(manual_pipeline_microarray.R, manual_pipeline_rnaseq.R, and
_multi variants for 2+ datasets) that run the full pipeline -
download through PPI - step by step using the same package functions
as the app.data.table::fread() with a path argument that function
does not have (it takes input). This broke reading of every downloaded
RNA-seq count file, direct NCBI counts and GEO supplementary files alike.MARCH1 -> 01-Mar)
were merged with all = TRUE without deduplicating first, so each
duplicate's row count multiplied across every per-sample file merged in
sequence. Both direct (NCBI) and supplementary RNA-seq download paths are
now verified working end to end.cbind row mismatch). The
combined matrix is aligned to shared symbols; Parallel keeps each GSE
at full coverage. The download log no longer calls this a network reset.keep_platforms_separate).*_raw_counts_*_NCBI.tsv.gz and microarray series-matrix files
across runs.rnaseq_counts TSV directly (skip GEOquery's broken
getRNASeqQuantResults() row.names join). Reject HTML/captcha files saved
with a .gz name. Fixes GSE50760 and similar series.rnaseq_counts via GEOquery. Fixes GSE50760 picking a 3-row metadata table.getRNASeqQuantResults() / NCBI rnaseq_counts first for
any human/mouse GSE with SRA data (fixes GSE50760 and similar RAW.tar-only series).rnaseq_counts fallback, and clearer errors when only FPKM/processed files exist.micro_data/rna_data cache by default (no wipe each run),
skip redundant phenodata enrichment and CEL supplementary files during download,
defer NCBI count fetch when GEO supplementary already has counts, and enrich
phenodata on the Groups tab instead of blocking Step 1. Control via
options(gexpipe.fast_download = TRUE) and options(gexpipe.clear_download_cache = TRUE).getGPL = FALSE (via .gexpipe_getgeo_series()) so series
like GSE89076 work when a corrupt/HTML GPL cache would otherwise break getGEO().runGExPipe(): do not pkgload::load_all() from an older source checkout when a
newer GExPipe is already installed (fixes Shiny using stale code from E:/GExPipe).RangedSummarizedExperiment from newer GEOquery
(not only ExpressionSet); fix series-matrix fallback search path and tab separator
when supplementary parsing is needed..gexpipe_geo_expr_matrix(), .gexpipe_geo_fdata(), and .gexpipe_geo_annotation()
helpers so expression, feature metadata, and platform IDs work for both
ExpressionSet and SummarizedExperiment objects.requireNamespace("rmda") / rmda code paths from the nomogram DCA module (fixes R CMD check WARNING about undeclared dependency).NEWS.md section titles to # GExPipe x.y.z so R can parse version history.rmda (Bioconductor CHECK ERROR). Nomogram DCA uses dcurves (already preferred in code).cat to msig_category so BiocCheck no longer flags a false cat() hit.0000-0003-2646-8106) in Authors@R.cre).GExPipe(original_paper)/ from the Bioconductor package tree (.Rbuildignore + .gitignore).importFrom for grDevices/graphics/stats/utils/shiny/ggplot2/DT/grid.utils::globalVariables() for NSE column names and Shiny symbols.sapply() with vapply(); prefer seq_len() / sample.int().print(p) with returning p in renderPlot.<<- via env boxes / reactiveValues assignment.gexp_fetch_geo_series_matrix_metadata call in validation server.set.seed() with withr::local_seed() / withr::with_seed() (BiocCheck).gexp_qc_build_sample_dataset_map man page example matrix dimensions.gexp_align_rnaseq_sample_names() runs after GEO download (GSE ID workflow), not manual data entry; fix example matrix dimensions in man page.man/gexp_align_rnaseq_sample_names.Rd example (fixes examples ERROR).gexp_align_rnaseq_sample_names() example matrix dimensions.R/ is not in installed layout.inst/shinyapp/server.R.install.packages() from GitHub bootstrap (BiocCheck compliance).test-bioc-review.R shinytest2 readme path for covr / installed-package test runs.vignettes/images/; maintainer-only notes removed.title column fallback for poorly annotated GEO series; optional group rename at Group Summary.shinytest2::use_shinytest2() in test mode so window.shinytest2.ready is set for AppDriver.GSE62646 by default)..gexpipe_shinytest2_poll_output(), .gexpipe_shinytest2_start_geo_download().inst/scripts/record-shinytest2-geo.R for interactive recording.shinytest2 workflow tests (tests/testthat/test-shiny-integration.R) and
helper-shinytest2.R for welcome → dashboard → QC navigation.shiny.testmode so shinytest2 sessions start quickly.inst/scripts/README-shinytest2.md.inst/shinyapp/global.R into R/gexpipe_shinyapp_bootstrap.R.suppressWarnings() / suppressMessages() in R/ with targeted quiet I/O helpers.tests/testthat/test-shiny-coverage.R and expanded bioc-review / app-builder tests for UI tabs, utils_shiny_app, and dummy_imports..gexpipe_best_version() for R 4.6+ (package_version comparison).vignettes/GExPipe.Rmd (screenshot paths,
internal vignette notes).vignettes/images/ with direct
knitr::include_graphics() calls.inst/scripts/README-vignette-screenshots.md.inst/shinyapp/server.R and ui.R now delegate to gexp_app_server() / gexp_app_ui()
instead of duplicating modular logic or calling source() on tab modules.gexpipe_spearman_cor() and removed suppressWarnings(cor(...)) from ML plots.tests/testthat/test-coverage-helpers.R for normalization, ID detection, WGCNA prep,
download overlap helpers, and UI/ML utilities.tests/testthat/test-pipeline-helpers.R for download/QC/classify helpers and
namespace-based server wiring.suppressMessages(capture.output(getGEO...)) with
.gexpipe_geo_quiet() and centralized count-file reads in .gexpipe_fread_counts().V2 sample names from headerless GEO count files; per-GSE labels in QC
outlier plots before normalization.BugReports now points to GitHub Issues (safarafique/GExPipe).inst/shinyapp/ to R/ (no runtime
source() / custom caching for tab modules).inst/scripts/make-vignette-extdata.R documenting synthetic vignette data.inst/pkg_versions.txt (versions are in DESCRIPTION).suppressWarnings() around namespace unloads; added tests for UI/server
builders, helpers, and pipeline wiring.