Validation and Test Runs¶
A Nextflow error that surfaces twenty minutes into a cloud run is expensive. Codaris checks a pipeline at three points before and during a run, each cheaper than the next, and each one is also available on its own.
1. Static validation of the generated files¶
nf_validate runs on every file the compiler writes and on every repair the fix loop proposes, before anything is written to disk. It is a set of targeted checks on the source text rather than a full parser, and each check names the rule it applies:
| Rule | What it catches |
|---|---|
undefined_process, undefined_channel | A process, subworkflow, or function that is called (including inline, inside an expression) without an include or a definition; a channel read before anything assigns it |
missing_include, include_name_missing, include_in_config | An include whose path does not exist, one that names something the target file does not define, or an include placed in a config file |
duplicate_include, duplicate_component_call | The same component included or called twice without an alias |
wrong_arity, wrong_arity_cross_file | A call whose argument count differs from the component's take: or input: block, in the same file or across files |
unknown_emit, unknown_emit_cross_file | X.out.name where the component emits no name |
typed_take | A subworkflow take: entry written with a type (val(meta)), which DSL2 rejects |
conditional_channel | A channel assigned in one branch of a conditional and read outside it |
process_section_order | A process whose input:, output:, and script: blocks are out of order |
groovy_reserved_keyword, include_path_has_extension, empty_subworkflow_main | Identifiers that collide with Groovy, include paths ending in .nf, and subworkflows with an empty main: |
container_latest_tag, dsl1_construct | A latest image tag; a DSL1 construct in a DSL2 file |
missing_container_directive, hardcoded_abs_path, unpinned_manifest_version | Warnings: a process without container, an absolute path in the source, a manifest without a pinned Nextflow version |
check_cross_file_calls checks a proposed file against its callers and callees with the proposal overlaid, so a renamed workflow whose include was not renamed is caught before the write.
codaris lint ./my-pipeline # nf-core pipelines lint, classified
codaris review ./my-pipeline --quick # the deterministic rule checkers and lint, no model
2. The compile gate¶
-preview makes Nextflow parse the whole pipeline and resolve every include, call, and argument count without running a task. It finishes in seconds and needs no containers or data. Inside a build the gate also checks that main.nf includes and calls the pipeline workflow, that the workflow calls at least one process, and that no planned module is included without being called; a build that fails either part goes to the fix loop with the named problem.
A quicker runtime check that still runs no tool:
-stub-run executes each process's stub: block, which exercises the channel wiring end to end with placeholder outputs.
3. The test run¶
codaris test ./my-pipeline
codaris test ./my-pipeline --profile test,docker --timeout 1800
codaris test ./my-pipeline --llm-repair --llm-repair-budget 3
codaris test checks readiness first (CPUs and memory against the profile's resourceLimits, the container engine, the sample sheet; --skip-readiness bypasses it), runs the test profile, and on failure classifies the error (out of memory, time limit, missing command, missing file, container failure, DSL2 wiring) and applies the matching fix within --fix-budget. With --llm-repair, failures without a deterministic fix are offered to a model as exact-substring edits, applied all or nothing, and rejected when nf_validate finds a new structural error; every attempt is recorded in .codaris/repair_log.json.
The default profile is test_codaris,docker: self-contained fixtures under assets/test_fixtures/, 2 CPUs, 4 GB, and 1 hour per task. conf/test.config from the nf-core template points at the nf-core test-datasets instead and needs network access.
Common failures and what Codaris does¶
| Failure | Cause | Response |
|---|---|---|
Missing process or function X() | A call without an include | The fix node adds include { X } from '<module path>' to the calling file when the module is installed |
expected N arguments but received M | A call whose arity drifted from the module | call_signature reports it before the preview; the fix node asks for a corrected file |
Channel already consumed | A queue channel read twice | Reported by the preview; the repair uses .collect() or a split channel |
| Task killed with exit 137 | Out of memory | Readiness and the run fixer raise the label's memory under the profile's resourceLimits and resume with -resume |
Unpinned container tag | :latest or no version tag on an image | Rule CR-0002 in review; remediation pins the module's declared tag after confirming it exists on the registry |