You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Line-level output diffing — under --diff-outputs, a file classified as changed that is text on both sides is now additionally diffed line by line
(reusing the same LineDiff engine as --diff-logs), so the report answers what changed rather than merely that it changed — a VCF, CSV, JSON, or
report file shows its added/removed lines inline. OutputComparator sniffs
the head of each changed file for a NUL byte; binary files fall back to the
existing size/hash verdict and produce no line diff. Reads are bounded: the
first --outputs-max-lines lines (new option, default 1000) and a hard byte
cap, so a huge file never blows up memory, with a truncated marker when a
cap dropped content. Surfaced in all three report formats (HTML unified-diff
pane with an added/removed line count, Markdown fenced diff block, and diff/linesAdded/linesRemoved/truncated fields on each output file in
JSON). This enriches the existing layer only — an output change still counts
toward the "identical" verdict and --fail-on-change exactly as before.
Config-provenance caveat — the configuration layer now inspects the git
state of the working tree it resolves config from and warns when that tree has
drifted from the revision a run was actually launched at. Because ConfigLoader rebuilds each run's effective config from the files as they
exist now, a run launched at commit A and re-run at commit B have their
configs both resolved against whatever is checked out now — silently masking
config differences driven by code changes between those revisions. The
metadata layer already records each run's revisionId; this compares it to
the current HEAD (via git rev-parse, prefix-matching abbreviated ids) and
also flags an uncommitted (dirty) working tree. When either run drifted, or
the tree is dirty, a prominent caveat is surfaced in all three report formats
(HTML warning banner, Markdown blockquote, and a configProvenance object
with driftedA/driftedB/workingTreeDirty/warning fields in JSON). Git
state is inspected best-effort — a non-git project, missing git, or a
command timeout degrades to "unknown" without breaking the diff. The caveat is
informational only: it never affects the "identical" verdict or --fail-on-change.
Software & versions diffing — a new always-on layer that compares, per
process, the distinct container image(s) and Conda package spec(s) that
process's tasks ran with in each run. Both values are read straight from the
run cache's trace records, so the layer needs no work directories and is
always computed. It answers "did a tool version change?" directly — e.g. biocontainers/fastqc:0.11.9 → biocontainers/fastqc:0.12.1 — instead of
leaving it buried in the per-task container field. A process present in only
one run is added/removed; a process in both whose container or Conda set
differs is flagged changed. A changed software environment counts toward the
"identical" verdict and --fail-on-change, which also makes a Conda-only
change (previously invisible to the task layer) break identity. Surfaced in
all three report formats (HTML section + summary card, Markdown section +
summary column, and a software array with a softwareChanged summary count
in JSON).
Output-file diffing — a new opt-in --diff-outputs layer that compares
the files each task matched in both runs wrote to its work directory,
classifying them as added / removed / changed / unchanged. Files are compared
by size first, then by a streamed SHA-256 for same-size files. Staged inputs
(symlinks) and Nextflow control files (.command.*, .exitcode) are skipped;
cache-resumed tasks that share a work directory short-circuit as identical.
Unlike the performance-regressions layer, an output-file change counts toward
the "identical" verdict and --fail-on-change, so this is the layer that
answers "did my pipeline actually produce different results?".
--outputs-max-bytes=<n> — caps the size of same-size files that are
hashed under --diff-outputs; larger files are reported content-unverified.
Default 0 means no limit.
Output diffs are surfaced in all three report formats (HTML, JSON, Markdown),
with an outputsChanged count in the JSON/HTML summary.
Failure / log diffing — a new opt-in --diff-logs layer that compares the
standard log files (.command.out, .command.err, .command.log) each
matched task wrote to its work directory, line by line. It surfaces the
exit-code and status change alongside the log contents, so a task that went
from exit 0 to exit 1 can be inspected side by side — answering not that a
task failed but what it printed before it did. Reads are bounded (tailed to
a line cap and a hard byte cap) so an enormous log never blows up memory, and
tasks sharing a work directory (cache-resumed) short-circuit as identical.
Because task stdout/stderr legitimately varies between runs (timestamps,
paths, ordering), this layer is informational only: it never affects the
"identical" verdict or --fail-on-change — the exit-code change already
captured by the per-task diff does that.
--logs-max-lines=<n> — keeps only the last <n> lines of each log file
before diffing under --diff-logs (default 200).
Log diffs are surfaced in all three report formats (HTML, JSON, Markdown),
with a logsChanged count in the JSON/HTML summary.
Fixed
--last and bare boolean flags dropped by the plugin launcher — Nextflow's plugin launcher rewrites forwarded arguments before they reach the verb: a
bare --flag arrives as --flag true, and --opt=value arrives
space-separated as --opt value. The diff parser assumed the inline =
form survived, so nextflow plugin nf-diff:diff --last reached it as ['--last', 'true'] and the injected true leaked into the positional list,
tripping the "--last cannot be combined with explicit run identifiers" guard.
The same latent bug affected --last=N and every bare boolean flag
(--fail-on-change, --verbose, --diff-outputs, --diff-logs). The parser
now tolerates the launcher-normalized forms, consuming an injected/inline true/false (or an integer for --last N) instead of treating it as a
positional run identifier.