Version 0.5.0
[0.5.0] - 2026-09-10
Added
-
Run metadata now compares the Nextflow version and runtime environment.
A run's Nextflow version can change between two runs and silently explain a
behaviour difference, but it was never surfaced.RunLoadernow reads the
run's data-lineageWorkflowRunrecord (via a new
LineageStore.environmentForSession()) and populatesRunSnapshotwith the
Nextflowversionandbuild, the container engine, and whether Wave and
Fusion were enabled.RunComparator.compareMetadata()adds these as metadata
rows (version/engine/Wave/Fusion are meaningful changes; the build number is
treated as context viaOBVIOUS_METADATA), so they render in every report
through the existing metadata table with no renderer-specific plumbing. Rows
appear only when a run recorded the value, so runs without a.lineage/store
(lineage.enabled=true, Nextflow 25.04+) are not padded with blanks.
Per-run plugin versions are deliberately not compared — Nextflow does not
persist them in the lineage store, the history file, or the task cache — and
the HTML metadata section now says so explicitly. -
The task layer now compares I/O counters and reports execution hardware.
RunComparator.TASK_FIELDSgained the disk-I/O counters (read_bytes,
write_bytes,syscr,syscw,vol_ctxt,inv_ctxt) and the
execution-environment fields (cpu_model,hostname,native_id). These are
already present in everyTraceRecordNextflow writes to the cache, so no new
data source is needed — the report simply stopped throwing them away. All nine
are added toOBVIOUS_TASK_FIELDS, so they surface for context (notably,
cpu_model/hostnameexplain a performance regression the perf layer already
flags — run B's task landing on a slower CPU) without flipping the "identical"
verdict or tripping--fail-on-changeunless--verboseis set.
Changed
-
The Parameters note now explains where non-launch params go. The HTML
report's params section only ever lists values from the launch command and
-params-file; params left at their defaults or set insidenextflow.config
/ an activated profile were silently absent, which read as "unset". The note
now states those are resolved config, not launch input, and links to the
Configuration layer where they actually appear. -
--helpnow documents the full exit-code contract.DiffPlugin.dispatch()
maps outcomes to four exit codes —0success,1runtime error,2usage
error,3--fail-on-changeon a difference — butusage()only mentioned
3(buried in the--fail-on-changeentry). A CI author reading--helphad
no way to tell2("I typed the command wrong") from1("the diff itself
failed"). A newExit codes:block inusage()spells out all four; doc-only,
no behaviour change. -
The process-wiring section now leads with a node-link diagram of the DAG,
not just a table of changed edges.renderDag()already had the full union
of process→process edges taggedUNCHANGED/ADDED/REMOVED
(RunComparatorbuilds it), but the report threw the unchanged edges away and
listed only added/removed rows — so the reader never saw where in the
topology a change sat. A newdagSvg()helper lays the union graph out with a
lightweight longest-path (Kahn) layering — columns = topological depth — and
emits a self-contained inline SVG: unchanged edges are neutral hairlines,
added edges solid green, removed edges dashed red, and a process appearing in
only one run gets a matching node outline. The layout is computed in Groovy so
the SVG needs no JavaScript or external assets (preserving the report's
no-network-assets guarantee), and the existing per-edge table is kept beneath
it as the precise detail and large-graph fallback. On the rich-report demo the
ALIGN→QCedge is now visibly rerouted through the newly insertedMARKDUP
node. OnlyHtmlReportRendererand its test changed; noDiffResult
accessors or other renderers were touched.
Changed
- HTML report summary reorganised from a flat wall of boxes into a scannable
hierarchy. The summary section previously rendered up to 14 identical,
equal-weightstatCards in a singleauto-fitgrid — the reader had to read
every box one by one to find the answer, with no cue that the first five were
a single task distribution and the rest were per-layer change counts.
renderSummary()now leads with a headline number (total task-level
differences, coloured by the identical/different verdict), collapses the four
mutually-exclusive task buckets (Changed / Only in A / Only in B / Unchanged)
into one stacked proportion bar with a counted legend — withRecomputed
demoted to an annotation since it is a cross-cut ofchanged, not a fifth
bucket — and groups the remaining diff-layer counts under labelled
Failures and Changes by layer bands. NewstatGroup()and
dispositionBar()helpers plus supporting CSS (.summary-headline,
.card-group,.disp*) reuse the existing colour tokens; no counts,
DiffResultaccessors, or other renderers changed.
Fixed
-
Report percentages are now locale-independent.
Format.signedPct()and
HtmlReportRenderer.fmt()formatted floating-point values with
String.format('%.1f', …)/String.format('%.2f', …), which use the JVM's
defaultLocale. Under a comma-decimal locale (e.g.pt_BR,de_DE) the
report emitted values like+12,5%and1,50instead of+12.5%and
1.50, corrupting the rendered percentages and breaking any downstream
numeric parsing that expects.as the decimal separator. Both call sites
now passLocale.ROOTso output is stable regardless of the host locale. -
The lineage-derived DAG now reads the
lineage/v1beta1store Nextflow
actually writes, instead of silently falling back to the symlink heuristic.
LineageStoreparsed a pre-v1beta1flat record shape — discriminator
type, withsessionId/name/inputat the top level. Current Nextflow
(25.04+) instead writes alineage/v1beta1envelope whose discriminator is
kindand whose payload is nested underspec. Every record therefore failed
thetype != 'TaskRun'guard,edgesForSession()returnednull, and
DagComparatorfell back to reconstructing edges from work-dir input symlinks
— so the authoritative-provenance path this class exists to provide was dead
against any real store, with nothing logged above debug level.LineageStore
now reads the discriminator viakindOf()(kind, falling back totype)
and the payload viaspecOf()(thespecmap, falling back to the record
itself), so both the current envelope and legacy flat stores reconstruct. The
existing unit tests were green only because they encoded the same obsolete
flat shape; a new fixture of realv1beta1records captured from a
rich-reportrun (src/test/resources/lineage/rich-report) now pins the
end-to-endINDEX_REF→ALIGN→MARKDUP→QC→MULTIQCreconstruction, alongside
directv1beta1envelope cases. Theexamples/rich-reportreport was
regenerated so its DAG layer reflects the authoritative lineage.
Full Changelog: v0.4.0...v0.5.0