Skip to content

Version 0.5.0

Choose a tag to compare

@mribeirodantas mribeirodantas released this 10 Sep 06:04
· 56 commits to main since this release

[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. RunLoader now reads the
    run's data-lineage WorkflowRun record (via a new
    LineageStore.environmentForSession()) and populates RunSnapshot with the
    Nextflow version and build, 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 via OBVIOUS_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_FIELDS gained 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 every TraceRecord Nextflow writes to the cache, so no new
    data source is needed — the report simply stopped throwing them away. All nine
    are added to OBVIOUS_TASK_FIELDS, so they surface for context (notably,
    cpu_model/hostname explain 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-change unless --verbose is 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 inside nextflow.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.

  • --help now documents the full exit-code contract. DiffPlugin.dispatch()
    maps outcomes to four exit codes — 0 success, 1 runtime error, 2 usage
    error, 3 --fail-on-change on a difference — but usage() only mentioned
    3 (buried in the --fail-on-change entry). A CI author reading --help had
    no way to tell 2 ("I typed the command wrong") from 1 ("the diff itself
    failed"). A new Exit codes: block in usage() 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 tagged UNCHANGED/ADDED/REMOVED
    (RunComparator builds 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 new dagSvg() 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→QC edge is now visibly rerouted through the newly inserted MARKDUP
    node. Only HtmlReportRenderer and its test changed; no DiffResult
    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-weight statCards in a single auto-fit grid — 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 — with Recomputed
    demoted to an annotation since it is a cross-cut of changed, not a fifth
    bucket — and groups the remaining diff-layer counts under labelled
    Failures and Changes by layer bands. New statGroup() and
    dispositionBar() helpers plus supporting CSS (.summary-headline,
    .card-group, .disp*) reuse the existing colour tokens; no counts,
    DiffResult accessors, 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
    default Locale. Under a comma-decimal locale (e.g. pt_BR, de_DE) the
    report emitted values like +12,5% and 1,50 instead 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 pass Locale.ROOT so output is stable regardless of the host locale.

  • The lineage-derived DAG now reads the lineage/v1beta1 store Nextflow
    actually writes, instead of silently falling back to the symlink heuristic.

    LineageStore parsed a pre-v1beta1 flat record shape — discriminator
    type, with sessionId/name/input at the top level. Current Nextflow
    (25.04+) instead writes a lineage/v1beta1 envelope whose discriminator is
    kind and whose payload is nested under spec. Every record therefore failed
    the type != 'TaskRun' guard, edgesForSession() returned null, and
    DagComparator fell 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 via kindOf() (kind, falling back to type)
    and the payload via specOf() (the spec map, 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 real v1beta1 records captured from a
    rich-report run (src/test/resources/lineage/rich-report) now pins the
    end-to-end INDEX_REF→ALIGN→MARKDUP→QC→MULTIQC reconstruction, alongside
    direct v1beta1 envelope cases. The examples/rich-report report was
    regenerated so its DAG layer reflects the authoritative lineage.

Full Changelog: v0.4.0...v0.5.0