Common Workflow Language (CWL) is a SPECIFICATION, not an engine - the fact from which every CWL decision follows. It deliberately splits the portable, strongly-typed workflow DESCRIPTION from its EXECUTION, so one unchanged document runs identically on any conforming runner (cwltool, Toil, Arvados, Calrissian). The entire value proposition - portability, static type-checking, vendor-neutrality, and standardized provenance - is a consequence of that split, and it is why CWL is the most verbose and most explicit of the four major systems: the verbosity buys analyzability and portability. The type system (File/Directory/record/enum/optional, plus secondaryFiles for index companions) catches wiring errors before any compute, and cwltool --provenance emits a CWLProv Research Object that hands an auditor exactly what ran - the reason CWL wins in regulated/clinical settings. Adopting CWL buys reproducible workflow LOGIC only: a clean typed DAG over unpinned tools is not reproducible, so containers, references, and seeds must be pinned separately.
# Reference runner (authoring, validation, provenance, local/CI)
pip install cwltool
# Container runtime (one of):
# Docker 24+ OR Singularity/Apptainer 3.8+
# At scale, install a production runner instead of cwltool:
pip install "toil[cwl]" # HPC/cloud batch (Slurm, Kubernetes, AWS)
# arvados-cwl-runner (Arvados, clinical/enterprise) or Calrissian (Kubernetes) as needed
Inputs: one or more .cwl documents (CommandLineTool/Workflow) plus a job/input object (job.yml) that supplies typed values. Container images pull from BioContainers (quay.io). Target cwlVersion: v1.2.
Tell your AI agent what you want to do: - "Wrap bwa-mem as a CWL CommandLineTool and declare the reference index as secondaryFiles" - "Build a CWL workflow that trims with fastp then quantifies with Salmon" - "Scatter my alignment step over paired FASTQ arrays with dotproduct" - "Move my ResourceRequirement from hints to requirements so the step stops getting OOM-killed" - "Validate this workflow and emit a CWLProv provenance object"
"Write a CWL CommandLineTool for samtools sort, declaring the input BAM's
.baias a secondaryFile and pinning the BioContainers image.""My reference FASTA needs a
.faiand a.dictcompanion - declare both as secondaryFiles and explain the caret rule for the.dictname."
"Build a CWL workflow that runs fastp then Salmon, wiring the trimmed reads from the first step into the second with outputSource."
"Scatter my per-sample alignment over paired R1/R2 arrays - which scatterMethod do I use, and what output shape do I get?"
"Audit my workflow for portability leaks: flag every InlineJavascriptRequirement and unpinned container, and rewrite the ${...} expressions as $(...) where possible."
"Run my workflow with
--provenanceand explain what the CWLProv Research Object contains for an audit."
"Validate this CWL locally with cwltool, then give me the toil-cwl-runner command to run the same document on our SLURM cluster."
outputBinding.glob or stdout.secondaryFiles on every indexed File input and output (.bai, .fai/.dict with the caret rule, .tbi) so runners co-stage companions.source/outputSource connections and --validate the contract before any compute.requirements, not hints, and reason about the workflow -> step -> tool override scope.scatterMethod deliberately (dotproduct vs flat_/nested_crossproduct) for the intended job cardinality and output shape.$(...) over ${...}, engine extensions kept in hints.secondaryFiles on any indexed File, or the runner stages the primary without its index and the tool fails with "index not found" at runtime.^ strips one extension: ^.dict on genome.fasta yields genome.dict, not genome.fasta.dict; each leading ^ strips one more.requirements (must hold) not hints (advisory) - a ResourceRequirement under hints can be silently ignored and OOM-kill the step.scatterMethod by intent: dotproduct zips equal-length arrays (N jobs); flat_/nested_crossproduct run all N x M pairs, differing only in output nesting.$(...) parameter references (no JS engine, statically analyzable, portable); treat each ${...} and its InlineJavascriptRequirement as a portability debt.DockerRequirement images by @sha256: digest, not :latest - a moving tag makes the result non-reproducible no matter how clean the DAG.outputSource: step/out; only tool outputs use outputBinding.glob - mixing them is a validation error.cwltool --provenance for a CWLProv Research Object in regulated/audited settings, and register on Dockstore/WorkflowHub (GA4GH TRS) for sharing.