Nicolas Vannieuwkerke
07/08/2026, 11:16 AMsamplesheetToRecords function which will convert a samplesheet to a list of Record types. However while doing this I'm also finding myself at the point where I need to decide how we want to use record types (especially with the meta map). So my question is: What would you prefer? (please react with the emoji)
Option 1️⃣ : All values will be available as a key-value pair in the Record, meaning that meta field and input field will be on the same level. (TBH this is my preferred way of working with records as this allow directly joining on specific meta keys using the join operator without having to do any preprocessing of the channel structure). Example:
[[id:'test', single_end:false], test_R1.fastq.gz, test_R2.fastq.gz]
Would become:
record(id: 'test', single_end:false, fastq_1:test_R1.fastq.gz, fastq_2:test_R2.fastq.gz)
Option 2️⃣ : We keep a meta key in the record that contains all meta values. (This would keep our channel structures relatively the same as before) Example:
[[id:'test', single_end:false], test_R1.fastq.gz, test_R2.fastq.gz]
Would become:
record(meta:[id:'test', single_end:false], fastq_1:test_R1.fastq.gz, fastq_2:test_R2.fastq.gz)
Option 3️⃣ : If you have a better idea please post it in this thread!Nicolas Vannieuwkerke
07/08/2026, 11:17 AMFamke Bäuerle
07/08/2026, 11:18 AMSimon Pearce
07/08/2026, 11:18 AMJames Fellows Yates
07/08/2026, 11:21 AMNicolas Vannieuwkerke
07/08/2026, 11:22 AMWould 1️⃣ also support other custom meta fields?Could you elaborate a bit more what you mean here?
Simon Pearce
07/08/2026, 11:22 AMbed, bam, index, if you would collide with anythingSimon Pearce
07/08/2026, 11:22 AMSimon Pearce
07/08/2026, 11:22 AMNicolas Vannieuwkerke
07/08/2026, 11:22 AMThe issue is what happens when they collide with the inputField names in samplesheets should be unique so there should never be any overlap between field names
Nicolas Vannieuwkerke
07/08/2026, 11:23 AMFamke Bäuerle
07/08/2026, 11:23 AMAdam Talbot
07/08/2026, 11:24 AMNicolas Vannieuwkerke
07/08/2026, 11:24 AM.join to merge channels with record types so you are in control of when an overlap happens. It's up to the pipeline developer to decide how to handle that IMONicolas Vannieuwkerke
07/08/2026, 11:25 AMOption 1 also doubles as an interface, i.e. a contract that says "this process will accept the input if it has fields x, y and z"Yes this would basically remove the distinction between meta field and value inputs of a process
Jonathan Manning
07/08/2026, 11:26 AMmeta was a hack we were forced into because 1 wasn't possible. Would be nice to kill it.Arthur
07/08/2026, 11:26 AMsamplesheetToRecords should make flat record and that then the pipeline developers can choose to group them in a map of the channel if the so choose (effectively convert one record type to another)Nicolas Vannieuwkerke
07/08/2026, 11:27 AMsamplesheetToRecords will just create a standard record, it's up to the pipeline developer to cast to the correct record type.Matthias De Smet
07/08/2026, 11:27 AMmeta key you essentially remove the whole idea of typing your input by creating a dumping ground for whatever you want in thereMatthias De Smet
07/08/2026, 11:27 AMArthur
07/08/2026, 11:27 AMext.args closures in configNicolas Vannieuwkerke
07/08/2026, 11:28 AMNicolas Vannieuwkerke
07/08/2026, 11:28 AMNicolas Vannieuwkerke
07/08/2026, 11:30 AMNicolas Vannieuwkerke
07/08/2026, 11:31 AMJames Fellows Yates
07/08/2026, 11:33 AMJames Fellows Yates
07/08/2026, 11:33 AMNicolas Vannieuwkerke
07/08/2026, 11:34 AMch1.join(ch2, by:id)Nicolas Vannieuwkerke
07/08/2026, 11:35 AMby option only takes one value, but I think we could make a suggestion for it to also allow a list of keysJames Fellows Yates
07/08/2026, 11:36 AMNicolas Vannieuwkerke
07/08/2026, 11:36 AMJames Fellows Yates
07/08/2026, 11:36 AMNicolas Vannieuwkerke
07/08/2026, 11:36 AMJames Fellows Yates
07/08/2026, 11:37 AMJames Fellows Yates
07/08/2026, 11:37 AMJames Fellows Yates
07/08/2026, 11:37 AMNicolas Vannieuwkerke
07/08/2026, 11:37 AMIgor Trujnara
07/08/2026, 11:37 AMNicolas Vannieuwkerke
07/08/2026, 11:38 AMfastq_1 and fasq_2) but indeed stuff like bam, cram, sam will need a common name which will be a bit more trickyJames Fellows Yates
07/08/2026, 11:39 AMJames Fellows Yates
07/08/2026, 11:40 AMNicolas Vannieuwkerke
07/08/2026, 11:41 AMJames Fellows Yates
07/08/2026, 11:41 AMJames Fellows Yates
07/08/2026, 11:42 AMJames Fellows Yates
07/08/2026, 11:42 AMIgor Trujnara
07/08/2026, 11:42 AMSome fields are easy (likeThat is a discussion independent of records, they are porting Sarek subworkflows to the modules repo and it's already coming upandfastq_1) but indeed stuff like bam, cram, sam will need a common name which will be a bit more trickyfasq_2
Nicolas Vannieuwkerke
07/08/2026, 11:45 AMMaxime Garcia
07/08/2026, 11:45 AMAdam Talbot
07/08/2026, 11:46 AMsample_info = record(foo: "x", bar: "y", baz: "z")
sample_record = record(id: "test", sample_info: sample_info, fastq_1:test_R1.fastq.gz, fastq_2:test_R2.fastq.gz)Nicolas Vannieuwkerke
07/08/2026, 11:47 AMAdam Talbot
07/08/2026, 11:47 AMid & sample_infoIgor Trujnara
07/08/2026, 11:48 AMch_samples.join(ch_genomes, by: { sample, genome -> sample.genome == genome.id })James Fellows Yates
07/08/2026, 11:49 AMYes record types are going to be a huge change indeed, hence why I wanted to start this discussion (thanks all for responding). And I'm not sure this will need a huge load of extra infrastructure, just some list with agreed upon names for specific file types?
Infrastructure I mean community infrastructure for agreeing to this. You say a list but would we need linting? Who decides on the definition? What if someone wants to use another name for the same thing etc.?
James Fellows Yates
07/08/2026, 11:51 AMMaxime Garcia
07/08/2026, 11:52 AMNicolas Vannieuwkerke
07/08/2026, 11:54 AMwhat about join with closure? e.g.Yeah feel free to comment on my issue with it, but I don't think this will be entirely necessary since you most likely will only join channels originating from the same channelch_samples.join(ch_genomes, by: { sample, genome -> sample.genome == genome.id })
Adam Talbot
07/08/2026, 11:55 AMNicolas Vannieuwkerke
07/08/2026, 11:55 AMInfrastructure I mean community infrastructure for agreeing to this. You say a list but would we need linting? Who decides on the definition? What if someone wants to use another name for the same thing etc.?We could keep this quite flexible though, I'm not set on being super strict on this. It would just be nice to have some common names for files that often are used (so we don't need to rename all the time)
Adam Talbot
07/08/2026, 11:56 AMSome fields are easy (likeAs someone who worked on libraries with 3-5 FASTQ files this was a nightmareandfastq_1)fasq_2
Nicolas Vannieuwkerke
07/08/2026, 11:57 AMBut aren't supposed to be able to update things one bit at a timeYeah but it's all connected, if you use a record type input for a process you also need to update your workflow to work with it
Adam Talbot
07/08/2026, 11:57 AMNicolas Vannieuwkerke
07/08/2026, 11:57 AMAdam Talbot
07/08/2026, 11:58 AMArthur
07/08/2026, 12:01 PMSorry I'm confused...why do we need a strict list forYou don't need one but for my money nf-core modules ceases to be that useful if I'm having to wrangle my records between each one so that I have the set of required keys. The current input situation has the advantage that the processes don't care what you refer to the items as outside of it (except keys within the meta map - where we have a very strict policy on hardcoded value use)inputsrecord keys?
James Fellows Yates
07/08/2026, 12:01 PMJames Fellows Yates
07/08/2026, 12:02 PMArthur
07/08/2026, 12:02 PMvcf_index, index, tbi, csi and I'm sure some othersNicolas Vannieuwkerke
07/08/2026, 12:03 PM.map to set the right structure which is kinda the same as having to rename fieldsJames Fellows Yates
07/08/2026, 12:03 PMJames Fellows Yates
07/08/2026, 12:03 PMJames Fellows Yates
07/08/2026, 12:04 PMAdam Talbot
07/08/2026, 12:14 PMIgor Trujnara
07/08/2026, 12:15 PMmashehu
07/08/2026, 12:16 PMvar, var2, etc. worked for meta 😜Igor Trujnara
07/08/2026, 12:18 PMJames Fellows Yates
07/08/2026, 12:19 PMJames Fellows Yates
07/08/2026, 12:20 PMJames Fellows Yates
07/08/2026, 12:20 PMArthur
07/08/2026, 12:21 PMIgor Trujnara
07/08/2026, 12:21 PMFlorian Wuennemann
07/08/2026, 12:21 PMFlorian Wuennemann
07/08/2026, 12:22 PMThey violate the idea the the process internals don't need to know about the externals@Arthur Curious why you think that this is a feature / requirement
Adam Talbot
07/08/2026, 12:23 PMArthur
07/08/2026, 12:25 PMFlorian Wuennemann
07/08/2026, 12:29 PMFlorian Wuennemann
07/08/2026, 12:30 PMJames Fellows Yates
07/08/2026, 12:32 PMinput.fastq_1, then the entire pipeline must also use input.fastq_1 throughout, if it doesn't want to have to rename record fields each time it passes a record to a new module?James Fellows Yates
07/08/2026, 12:34 PMNicolas Vannieuwkerke
07/08/2026, 12:35 PMFlorian Wuennemann
07/08/2026, 12:36 PMNicolas Vannieuwkerke
07/08/2026, 12:37 PMJames Fellows Yates
07/08/2026, 12:38 PMJames Fellows Yates
07/08/2026, 12:39 PMNicolas Vannieuwkerke
07/08/2026, 12:39 PMext.args thing?Simon Pearce
07/08/2026, 12:40 PMJames Fellows Yates
07/08/2026, 12:40 PMFlorian Wuennemann
07/08/2026, 12:41 PMI don't necessarily agree eitherSimon not agreeing really threw me for a loop 😉 😄
Florian Wuennemann
07/08/2026, 12:41 PMSimon Pearce
07/08/2026, 12:41 PMSimon Pearce
07/08/2026, 12:41 PMFlorian Wuennemann
07/08/2026, 12:42 PMSimon Pearce
07/08/2026, 12:43 PMFlorian Wuennemann
07/08/2026, 12:46 PMWhat happens if your (currently) meta variables are also inputs or outputs of the module code?Why would this be potentially a problem? It's just other fields now pointing to files isn't it?
Simon Pearce
07/08/2026, 12:51 PMtuple (meta, file)
and outputs
tuple (meta, foo)
If we had meta.foo on the input record, then I think foo gets overwrittenNicolas Vannieuwkerke
07/08/2026, 12:53 PMWhat happens if your (currently) meta variables are also inputs or outputs of the module code?Can you give an example where this is the problem? I can't remember a single moment where my meta fields and process input names overlapped and where it would cause a problem
Florian Wuennemann
07/08/2026, 12:55 PMmeta. to be fair, you can still do meta_foo as the record type. I guess this brings a good question though. Do most people like the fact that having meta. identifies meta values as metadata, in which case, keeping it more for naming sake than functionality might make sense...Nicolas Vannieuwkerke
07/08/2026, 12:56 PMJames Fellows Yates
07/08/2026, 12:56 PMJames Fellows Yates
07/08/2026, 12:57 PMNicolas Vannieuwkerke
07/08/2026, 12:57 PMNicolas Vannieuwkerke
07/08/2026, 12:57 PMJames Fellows Yates
07/08/2026, 12:57 PMJames Fellows Yates
07/08/2026, 12:58 PMNicolas Vannieuwkerke
07/08/2026, 12:58 PMFlorian Wuennemann
07/08/2026, 12:59 PMNicolas Vannieuwkerke
07/08/2026, 1:00 PMSimon Pearce
07/08/2026, 1:00 PMCan you give an example where this is the problem? I can't remember a single moment where my meta fields and process input names overlapped and where it would cause a problemI don't have an example to hand. And there might not be one
Florian Wuennemann
07/08/2026, 1:02 PMFlorian Wuennemann
07/08/2026, 1:03 PMNicolas Vannieuwkerke
07/08/2026, 1:04 PMAdam Talbot
07/08/2026, 1:11 PMWhat we've basically concocted here is functions taking dictionaries with arbitrarily required keys - something which is typically a bad design.As opposed to an array of tuples only accessed by index 😉
Nicolas Vannieuwkerke
07/08/2026, 1:12 PMMatthias De Smet
07/08/2026, 1:15 PMCan you give an example where this is the problem? I can’t remember a single moment where my meta fields and process input names overlapped and where it would cause a problemYeah I can 😓 Check our own preproc pipeline, where there’s
fasta in the meta (so we can have a genome per sample) and then the fasta inputs for the modulesNicolas Vannieuwkerke
07/08/2026, 1:15 PMNicolas Vannieuwkerke
07/08/2026, 1:16 PMMatthias De Smet
07/08/2026, 1:18 PMNiklas Schandry
07/13/2026, 7:29 AMmeta ) and not enforce keys on any other fields. E.g. What if my pipeline takes two different types of short reads (e.g. genomic, HiC), I cannot put both of them into fastq_1,2 ? There is also the situation where I might store stuff in meta, that is not used as an input, but comes in via ext.args through config, how can this be done with scheme 1?Nicolas Vannieuwkerke
07/13/2026, 7:31 AMext.args block. having two types of fastqs is something completely different and would return us back to the naming discussion 😁Niklas Schandry
07/13/2026, 7:32 AMNicolas Vannieuwkerke
07/13/2026, 7:33 AMNicolas Vannieuwkerke
07/13/2026, 7:33 AMNiklas Schandry
07/13/2026, 7:33 AMNicolas Vannieuwkerke
07/13/2026, 7:34 AMArthur
07/13/2026, 7:40 AMmeta as a nested record.
A more specific record type will always match a generic one I think, so pipelines can effectively just then define their own meta record type with any of their required keys there. The process itself obviously wouldn’t be doing any type-checking of it, but presumably the typing of the channel itself would effectively gate it anyway?
And then we can declare meta as a reserved key within nf-core, which is way simpler than worrying about arbitrary name clashesArthur
07/13/2026, 7:43 AMid to a first-class record field tooNicolas Vannieuwkerke
07/13/2026, 7:44 AMJames Fellows Yates
07/13/2026, 7:45 AMNicolas Vannieuwkerke
07/13/2026, 7:46 AMSimon Pearce
07/13/2026, 8:37 AMOn the other hand using record types to the fullest will make our pipeline development significantly easier.I remain to be convinced by this, so some examples would be helpful
Arthur
07/13/2026, 8:39 AMNicolas Vannieuwkerke
07/13/2026, 8:41 AMSimon Pearce
07/13/2026, 9:02 AMNicolas Vannieuwkerke
07/13/2026, 9:03 AMSimon Pearce
07/13/2026, 9:11 AMSimon Pearce
07/13/2026, 9:12 AMSimon Pearce
07/13/2026, 9:13 AMArthur
07/13/2026, 9:15 AMIgor Trujnara
07/13/2026, 9:17 AMArthur
07/13/2026, 9:18 AMSimon Pearce
07/13/2026, 9:19 AMSimon Pearce
07/13/2026, 9:20 AMNicolas Vannieuwkerke
07/13/2026, 9:20 AMSimon Pearce
07/13/2026, 9:21 AMNicolas Vannieuwkerke
07/13/2026, 9:23 AMBen Sherman
07/13/2026, 3:01 PMBen Sherman
07/13/2026, 3:09 PMBen Sherman
10/02/2026, 11:13 AMargs param provides per-pipeline overrides for tool args. Per-sample args overrides are supplied as samplesheet columns
See the PR description for more details. Feel free to comment there with your feedback
I don't need you to trust me, but I do need you to approach this issue with an open mind. The current patterns are shaped by the old constraints of DSL2. With static typing those constraints have been lifted and new approaches are possible. But we have to be willing to explore them fully, not just speculateNiklas Schandry
10/02/2026, 11:59 AMMahesh Binzer-Panchal
10/02/2026, 12:00 PMArthur
10/02/2026, 12:28 PMext means an off-the-shelf pipeline is likely to be less flexible or require a much greater amount of boilerplate?Matthieu Muffato
10/02/2026, 12:35 PMid, prefix, suffix, and args. So in other words, explicitly moving all the accepted meta keys from the nf-core guidelines into explicit universal record attributesNiklas Schandry
10/02/2026, 12:35 PMNiklas Schandry
10/02/2026, 12:50 PMext are not an option, is this incompatibility ideological, to achieve the perfectly shaped input row, or is it a language limitation? I guess what I am asking, would something like: def args = args && task.ext.args ? args.join(task.ext.args) : args ?: task.ext.args ?: '' be an option?Igor Trujnara
10/02/2026, 1:08 PMmeta, since it was a hack anyway, but I don't think we want to lose ext with all its options. Being able to "break open" a pipeline when needed is one of the most powerful aspects of our setup and it would be a shame to get rid of it. For the more advanced user, acting directly on the specific module is the natural thing to do and it eliminates all the layers of complexity in between.Phil Ewels
10/02/2026, 1:10 PMuser, and not developerJim Downie
10/02/2026, 2:48 PMtask.ext.args override for power users!Niklas Schandry
10/02/2026, 3:21 PMAdam Talbot
10/02/2026, 3:34 PMJim Downie
10/02/2026, 3:36 PMAdam Talbot
10/02/2026, 3:37 PMAdam Talbot
10/02/2026, 3:48 PMFields will be silently dropped. So I think we need the meta map.Confirmed, this isn't true
Adam Talbot
10/02/2026, 3:50 PMmeta item: gist.github.com/adamrtalbot/…Adam Talbot
10/02/2026, 3:51 PMJim Downie
10/02/2026, 3:51 PMinput:
record(
id: String,
reads: List<Path>,
bwa_index: Path,
fasta: Path?,
args: String?,
args2: String?,
prefix: String?
)
sort_bam: Boolean
output:
record(
id : id,
bam : file("*.bam", optional: true),
cram : file("*.cram", optional: true),
sam : file("*.sam", optional: true),
csi : file("*.csi", optional: true),
crai : file("*.crai", optional: true)
)
If the input record has some other field, a piece of important and identifying sample metadata, it won't be included in the output.Jim Downie
10/02/2026, 3:51 PMAdam Talbot
10/02/2026, 3:51 PMJim Downie
10/02/2026, 3:53 PMinput:
mysample: record(
id: String,
reads: List<Path>,
bwa_index: Path,
fasta: Path?,
args: String?,
args2: String?,
prefix: String?
)
output:
mysample + record(...)
But that way lies ever-growing-sized records and name collisions. Really we only want to passage metadata.Adam Talbot
10/02/2026, 3:54 PMrecord Meta {
id: String
tumour: bool
other: String
}
record Sample {
meta: Meta
bam: Path
}
input:
sample: Sample
output:
record(meta: sample.meta, results: file('output.txt'))Jim Downie
10/02/2026, 3:54 PMAdam Talbot
10/02/2026, 3:55 PMAdam Talbot
10/02/2026, 3:58 PMtask.ext.argsBen Sherman
10/02/2026, 10:25 PMext in general but the fact that ext is the only way to set things like args / prefix / suffix:
• pipelines must spill their guts into config files in order to set pipeline-level defaults. this makes pipeline composition more difficult because module config doesn't travel with the process/workflow it's trying to configure
• same for workflow modules, if an nf-core subworkflow has some module config, I have to remember to include that config when I use the module. and I have to deal with process selectors, etc
• if I run a process/workflow directly via nextflow module run, I can't set the tool args via param because it isn't declared as a process input. I have to prepare a config file before every command, again dealing with process selectors etcBen Sherman
10/02/2026, 10:32 PMargs ?: task.ext.args ?: <default>
so we declare every ext setting as a process input but we still allow a runtime override from config. the process input means we can move the pipeline's ext config into pipeline code, which is all I really care about
that also implies we have to keep meta. it's the only way for a user to access metadata fields from config. for me this is suboptimal but not really a problem. you might be able to eliminate it by writing the process inputs/outputs differently but I don't think it's worthwhile
so, here is take 2: github.com/nf-core/methylseq/pull/626Ben Sherman
10/02/2026, 10:36 PMNicolas Vannieuwkerke
10/05/2026, 8:50 AMNicolas Vannieuwkerke
10/05/2026, 8:51 AMBen Sherman
10/05/2026, 1:23 PMmap operations. I'm also looking into joining on a closure predicate, but that won't be in 26.10Jim Downie
10/05/2026, 1:24 PMJim Downie
10/05/2026, 1:24 PMNicolas Vannieuwkerke
10/05/2026, 1:25 PMmeta map in the record 😛