Skip to content

Conversation

@pinin4fjords
Copy link
Collaborator

⚠️ Draft Material - Not a Merge Candidate

This PR contains custom training content for specific teaching sessions. It is not intended to be merged into the main training materials at this time.

Summary

Three new side quests covering advanced Nextflow topics:

1. Workflow Output Definitions (45 mins)

  • New publish: and output {} block syntax (replacing publishDir)
  • Dynamic paths based on metadata
  • Automatic index file generation (CSV/JSON manifests)

2. Running on Cloud Executors (30 mins)

  • Executor concepts and available options
  • AWS Batch configuration patterns
  • S3 integration, profiles, cost management
  • Conceptual only - no cloud account required

3. Plugin Development (90 mins)

  • Full hands-on plugin creation from scratch
  • Custom functions with @Function annotation
  • Build, test, and local installation
  • Complete working nf-greeting plugin solution included

Additional Changes

  • Added Gradle 8.11 to devcontainer for plugin development support
  • All examples use greetings pipeline (consistent with Hello Nextflow)

Testing

  • ✅ All Nextflow scripts tested and working
  • ✅ Plugin builds and runs successfully
  • ✅ Validation checks pass (heading numbering, formatting)

Notes

Keep on branch for custom training sessions. May be adapted for general availability later.

🤖 Generated with Claude Code

Three new side quests for advanced Nextflow topics:

1. Workflow Output Definitions (45 mins)
   - New publish/output block syntax
   - Dynamic paths based on metadata
   - Index file generation

2. Running on Cloud Executors (30 mins)
   - Executor concepts and options
   - AWS Batch configuration patterns
   - S3 integration and cost management
   - Conceptual/config-focused (no cloud account needed)

3. Plugin Development (90 mins)
   - Full hands-on plugin creation
   - Custom functions with @function annotation
   - Build, test, and use locally
   - Complete working nf-greeting plugin solution

Also adds Gradle to devcontainer for plugin development support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@netlify
Copy link

netlify bot commented Jan 7, 2026

Deploy Preview for nextflow-training ready!

Name Link
🔨 Latest commit e00b7f0
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-training/deploys/696150e3d5758a0008074465
😎 Deploy Preview https://deploy-preview-756--nextflow-training.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

Nextflow linting complete!

❌ 5 files had 22 errors
✅ 170 files had no errors
🔧 153 files would be changed by auto-formatting

💡 Tip: Click filename locations to go directly to that code.

View all 22 issues
Type Location Message
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:11:1 Invalid include source: '/home/runner/work/training/training/hello-nextflow/solutions/5-hello-containers/modules/sayHello.nf'
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:12:1 Invalid include source: '/home/runner/work/training/training/hello-nextflow/solutions/5-hello-containers/modules/convertToUpper.nf'
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:13:1 Invalid include source: '/home/runner/work/training/training/hello-nextflow/solutions/5-hello-containers/modules/collectGreetings.nf'
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:24:5 sayHello is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:27:5 convertToUpper is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:27:20 sayHello is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:30:5 collectGreetings is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:30:22 convertToUpper is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:33:5 collectGreetings is not defined
Error hello-nextflow/solutions/5-hello-containers/hello-containers-2.nf:36:11 collectGreetings is not defined
Error hello-nf-core/solutions/core-hello-part2/nextflow.config:154:17 Invalid include source: '/home/runner/work/training/training/hello-nf-core/solutions/core-hello-part2/conf/test_full.config'
Error side-quests/solutions/workflow_outputs/main.nf:4:1 Unrecognized feature flag 'nextflow.preview.output'
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:1:1 Invalid include source: '/home/runner/work/training/training/side-quests/solutions/workflows_of_workflows/modules/validate_name.nf'
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:2:1 Invalid include source: '/home/runner/work/training/training/side-quests/solutions/workflows_of_workflows/modules/say_hello.nf'
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:3:1 Invalid include source: '/home/runner/work/training/training/side-quests/solutions/workflows_of_workflows/modules/timestamp_greeting.nf'
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:11:24 VALIDATE_NAME is not defined
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:12:24 SAY_HELLO is not defined
Error side-quests/solutions/workflows_of_workflows/workflows/greeting.nf:13:26 TIMESTAMP_GREETING is not defined
Error side-quests/solutions/workflows_of_workflows/workflows/transform.nf:1:1 Invalid include source: '/home/runner/work/training/training/side-quests/solutions/workflows_of_workflows/modules/say_hello_upper.nf'
Error side-quests/solutions/workflows_of_workflows/workflows/transform.nf:2:1 Invalid include source: '/home/runner/work/training/training/side-quests/solutions/workflows_of_workflows/modules/reverse_text.nf'
Error side-quests/solutions/workflows_of_workflows/workflows/transform.nf:10:20 SAY_HELLO_UPPER is not defined
Error side-quests/solutions/workflows_of_workflows/workflows/transform.nf:11:23 REVERSE_TEXT is not defined
View formatting changes
FileDiff
hello-nextflow/solutions/1-hello-world/hello-world-3.nf
View
@@ -8,7 +8,7 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     output:
-        path 'output.txt'
+    path 'output.txt'
 
     script:
     """
hello-nextflow/solutions/1-hello-world/hello-world-4.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path 'output.txt'
+    path 'output.txt'
 
     script:
     """
-    echo '$greeting' > output.txt
+    echo '${greeting}' > output.txt
... (truncated)
hello-nextflow/solutions/2-hello-channels/hello-channels-1.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path 'output.txt'
+    path 'output.txt'
 
     script:
     """
-    echo '$greeting' > output.txt
+    echo '${greeting}' > output.txt
... (truncated)
hello-nextflow/solutions/2-hello-channels/hello-channels-2.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/2-hello-channels/hello-channels-3.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/2-hello-channels/hello-channels-4.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/3-hello-workflow/hello-workflow-1.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/3-hello-workflow/hello-workflow-2.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/3-hello-workflow/hello-workflow-3.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/3-hello-workflow/hello-workflow-4.nf
View
@@ -8,14 +8,14 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/4-hello-modules/hello-modules-2.nf
View
@@ -8,14 +8,14 @@ process convertToUpper {
     publishDir 'results', mode: 'copy'
 
     input:
-        path input_file
+    path input_file
 
     output:
-        path "UPPER-${input_file}"
+    path "UPPER-${input_file}"
 
     script:
     """
-    cat '$input_file' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
+    cat '${input_file}' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
... (truncated)
hello-nextflow/solutions/4-hello-modules/hello-modules-3.nf
View
@@ -8,15 +8,15 @@ process collectGreetings {
     publishDir 'results', mode: 'copy'
 
     input:
-        path input_files
-        val batch_name
+    path input_files
+    val batch_name
 
     output:
-        path "COLLECTED-${batch_name}-output.txt" , emit: outfile
-        val count_greetings , emit: count
+    path "COLLECTED-${batch_name}-output.txt", emit: outfile
+    val count_greetings, emit: count
 
... (truncated)
hello-nextflow/solutions/4-hello-modules/hello-modules-4.nf
View
@@ -15,8 +15,8 @@ workflow {
 
     // create a channel for inputs from a CSV file
     greeting_ch = channel.fromPath(params.greeting)
-                        .splitCsv()
-                        .map { line -> line[0] }
+        .splitCsv()
+        .map { line -> line[0] }
 
     // emit a greeting
     sayHello(greeting_ch)
@@ -28,5 +28,5 @@ workflow {
     collectGreetings(convertToUpper.out.collect(), params.batch)
 
     // emit a message about the size of the batch
... (truncated)
hello-nextflow/solutions/4-hello-modules/modules/collectGreetings.nf
View
@@ -6,15 +6,15 @@ process collectGreetings {
     publishDir 'results', mode: 'copy'
 
     input:
-        path input_files
-        val batch_name
+    path input_files
+    val batch_name
 
     output:
-        path "COLLECTED-${batch_name}-output.txt" , emit: outfile
-        val count_greetings , emit: count
+    path "COLLECTED-${batch_name}-output.txt", emit: outfile
+    val count_greetings, emit: count
 
... (truncated)
hello-nextflow/solutions/4-hello-modules/modules/convertToUpper.nf
View
@@ -8,13 +8,13 @@ process convertToUpper {
     publishDir 'results', mode: 'copy'
 
     input:
-        path input_file
+    path input_file
 
     output:
-        path "UPPER-${input_file}"
+    path "UPPER-${input_file}"
 
     script:
     """
-    cat '$input_file' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
+    cat '${input_file}' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
... (truncated)
hello-nextflow/solutions/4-hello-modules/modules/sayHello.nf
View
@@ -8,13 +8,13 @@ process sayHello {
     publishDir 'results', mode: 'copy'
 
     input:
-        val greeting
+    val greeting
 
     output:
-        path "${greeting}-output.txt"
+    path "${greeting}-output.txt"
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
... (truncated)
hello-nextflow/solutions/5-hello-containers/modules/cowpy.nf
View
@@ -8,14 +8,14 @@ process cowpy {
     container 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273'
 
     input:
-        path input_file
-        val character
+    path input_file
+    val character
 
     output:
-        path "cowpy-${input_file}"
+    path "cowpy-${input_file}"
 
     script:
     """
... (truncated)
hello-nextflow/solutions/6-hello-config/modules/cowpy.nf
View
@@ -9,14 +9,14 @@ process cowpy {
     conda 'conda-forge::cowpy==1.1.5'
 
     input:
-        path input_file
-        val character
+    path input_file
+    val character
 
     output:
-        path "cowpy-${input_file}"
+    path "cowpy-${input_file}"
 
     script:
     """
... (truncated)
hello-nf-core/solutions/composable-hello/hello.nf
View
@@ -14,9 +14,7 @@ include { collectGreetings } from './modules/collectGreetings.nf'
 include { cowpy } from './modules/cowpy.nf'
 
 workflow HELLO {
-
     take:
-    // channel of greetings
     greeting_ch
 
     main:
hello-nf-core/solutions/composable-hello/main.nf
View
@@ -9,12 +9,12 @@ params.greeting = 'greetings.csv'
 workflow {
     // create a channel for inputs from a CSV file
     greeting_ch = channel.fromPath(params.greeting)
-                        .splitCsv()
-                        .map { line -> line[0] }
+        .splitCsv()
+        .map { line -> line[0] }
 
     // call the imported workflow on the channel of greetings
     HELLO(greeting_ch)
 
     // view the outputs emitted by the workflow
-    HELLO.out.view { output -> "Output: $output" }
+    HELLO.out.view { output -> "Output: ${output}" }
... (truncated)
hello-nf-core/solutions/composable-hello/modules/collectGreetings.nf
View
@@ -10,8 +10,8 @@ process collectGreetings {
     val batch_name
 
     output:
-    path "COLLECTED-${batch_name}-output.txt" , emit: outfile
-    val count_greetings , emit: count
+    path "COLLECTED-${batch_name}-output.txt", emit: outfile
+    val count_greetings, emit: count
 
     script:
     count_greetings = input_files.size()
hello-nf-core/solutions/composable-hello/modules/convertToUpper.nf
View
@@ -15,6 +15,6 @@ process convertToUpper {
 
     script:
     """
-    cat '$input_file' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
+    cat '${input_file}' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
     """
 }
hello-nf-core/solutions/composable-hello/modules/cowpy.nf
View
@@ -17,6 +17,6 @@ process cowpy {
 
     script:
     """
-    cat $input_file | cowpy -c "$character" > cowpy-${input_file}
+    cat ${input_file} | cowpy -c "${character}" > cowpy-${input_file}
     """
 }
hello-nf-core/solutions/composable-hello/modules/sayHello.nf
View
@@ -15,6 +15,6 @@ process sayHello {
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
     """
 }
hello-nf-core/solutions/core-hello-part2/conf/base.config
View
@@ -11,13 +11,13 @@
 process {
 
     // TODO nf-core: Check the defaults for all processes
-    cpus   = { 1      * task.attempt }
-    memory = { 6.GB   * task.attempt }
-    time   = { 4.h    * task.attempt }
+    cpus = { 1 * task.attempt }
+    memory = { 6.GB * task.attempt }
+    time = { 4.h * task.attempt }
 
     errorStrategy = { task.exitStatus in ((130..145) + 104 + 175) ? 'retry' : 'finish' }
-    maxRetries    = 1
-    maxErrors     = '-1'
+    maxRetries = 1
... (truncated)
hello-nf-core/solutions/core-hello-part2/conf/modules.config
View
@@ -15,7 +15,6 @@ process {
     publishDir = [
         path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" },
         mode: params.publish_dir_mode,
-        saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
+        saveAs: { filename -> filename.equals('versions.yml') ? null : filename },
     ]
-
 }
hello-nf-core/solutions/core-hello-part2/conf/test.config
View
@@ -14,18 +14,18 @@ process {
     resourceLimits = [
         cpus: 2,
         memory: '4.GB',
-        time: '1.h'
+        time: '1.h',
     ]
 }
 
 params {
-    config_profile_name        = 'Test profile'
+    config_profile_name = 'Test profile'
     config_profile_description = 'Minimal test dataset to check pipeline function'
 
     // Input data
... (truncated)
hello-nf-core/solutions/core-hello-part2/main.nf
View
@@ -13,9 +13,9 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
 
-include { HELLO  } from './workflows/hello'
+include { HELLO } from './workflows/hello'
 include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_hello_pipeline'
-include { PIPELINE_COMPLETION     } from './subworkflows/local/utils_nfcore_hello_pipeline'
+include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_hello_pipeline'
 /*
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     NAMED WORKFLOWS FOR PIPELINE
@@ -26,7 +26,6 @@ include { PIPELINE_COMPLETION     } from './subworkflows/local/utils_nfcore_hell
 // WORKFLOW: Run main analysis pipeline depending on type of input
 //
... (truncated)
hello-nf-core/solutions/core-hello-part2/modules/local/collectGreetings.nf
View
@@ -10,8 +10,8 @@ process collectGreetings {
     val batch_name
 
     output:
-    path "COLLECTED-${batch_name}-output.txt" , emit: outfile
-    val count_greetings , emit: count
+    path "COLLECTED-${batch_name}-output.txt", emit: outfile
+    val count_greetings, emit: count
 
     script:
     count_greetings = input_files.size()
hello-nf-core/solutions/core-hello-part2/modules/local/convertToUpper.nf
View
@@ -15,6 +15,6 @@ process convertToUpper {
 
     script:
     """
-    cat '$input_file' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
+    cat '${input_file}' | tr '[a-z]' '[A-Z]' > 'UPPER-${input_file}'
     """
 }
hello-nf-core/solutions/core-hello-part2/modules/local/cowpy.nf
View
@@ -17,6 +17,6 @@ process cowpy {
 
     script:
     """
-    cat $input_file | cowpy -c "$character" > cowpy-${input_file}
+    cat ${input_file} | cowpy -c "${character}" > cowpy-${input_file}
     """
 }
hello-nf-core/solutions/core-hello-part2/modules/local/sayHello.nf
View
@@ -15,6 +15,6 @@ process sayHello {
 
     script:
     """
-    echo '$greeting' > '$greeting-output.txt'
+    echo '${greeting}' > '${greeting}-output.txt'
     """
 }
hello-nf-core/solutions/core-hello-part2/subworkflows/local/utils_nfcore_hello_pipeline/main.nf
View
@@ -8,13 +8,13 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
 
-include { UTILS_NFSCHEMA_PLUGIN     } from '../../nf-core/utils_nfschema_plugin'
-include { paramsSummaryMap          } from 'plugin/nf-schema'
-include { samplesheetToList         } from 'plugin/nf-schema'
-include { paramsHelp                } from 'plugin/nf-schema'
-include { completionSummary         } from '../../nf-core/utils_nfcore_pipeline'
-include { UTILS_NFCORE_PIPELINE     } from '../../nf-core/utils_nfcore_pipeline'
-include { UTILS_NEXTFLOW_PIPELINE   } from '../../nf-core/utils_nextflow_pipeline'
+include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin'
+include { paramsSummaryMap } from 'plugin/nf-schema'
+include { samplesheetToList } from 'plugin/nf-schema'
+include { paramsHelp } from 'plugin/nf-schema'
... (truncated)
hello-nf-core/solutions/core-hello-part2/subworkflows/nf-core/utils_nextflow_pipeline/main.nf
View
@@ -10,9 +10,9 @@
 
 workflow UTILS_NEXTFLOW_PIPELINE {
     take:
-    print_version        // boolean: print version
-    dump_parameters      // boolean: dump parameters
-    outdir               //    path: base directory used to publish pipeline results
+    print_version // boolean: print version
+    dump_parameters // boolean: dump parameters
+    outdir //    path: base directory used to publish pipeline results
     check_conda_channels // boolean: check conda channels
 
     main:
@@ -72,10 +72,10 @@ def getWorkflowVersion() {
 //
... (truncated)
hello-nf-core/solutions/core-hello-part2/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config
View
@@ -1,9 +1,9 @@
 manifest {
-    name            = 'nextflow_workflow'
-    author          = """nf-core"""
-    homePage        = 'https://127.0.0.1'
-    description     = """Dummy pipeline"""
+    name = 'nextflow_workflow'
+    author = """nf-core"""
+    homePage = 'https://127.0.0.1'
+    description = """Dummy pipeline"""
     nextflowVersion = '!>=23.04.0'
-    version         = '9.9.9'
-    doi             = 'https://doi.org/10.5281/zenodo.5070524'
+    version = '9.9.9'
+    doi = 'https://doi.org/10.5281/zenodo.5070524'
... (truncated)
hello-nf-core/solutions/core-hello-part2/subworkflows/nf-core/utils_nfcore_pipeline/main.nf
View
@@ -125,12 +125,12 @@ def paramsSummaryMultiqc(summary_params) {
         }
 
     def yaml_file_text = "id: '${workflow.manifest.name.replace('/', '-')}-summary'\n" as String
-    yaml_file_text     += "description: ' - this information is collected when the pipeline is started.'\n"
-    yaml_file_text     += "section_name: '${workflow.manifest.name} Workflow Summary'\n"
-    yaml_file_text     += "section_href: 'https://github.com/${workflow.manifest.name}'\n"
-    yaml_file_text     += "plot_type: 'html'\n"
-    yaml_file_text     += "data: |\n"
-    yaml_file_text     += "${summary_section}"
+    yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n"
+    yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n"
+    yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n"
+    yaml_file_text += "plot_type: 'html'\n"
+    yaml_file_text += "data: |\n"
... (truncated)
hello-nf-core/solutions/core-hello-part2/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config
View
@@ -1,9 +1,9 @@
 manifest {
-    name            = 'nextflow_workflow'
-    author          = """nf-core"""
-    homePage        = 'https://127.0.0.1'
-    description     = """Dummy pipeline"""
-    nextflowVersion  = '!>=23.04.0'
-    version         = '9.9.9'
-    doi             = 'https://doi.org/10.5281/zenodo.5070524'
+    name = 'nextflow_workflow'
+    author = """nf-core"""
+    homePage = 'https://127.0.0.1'
+    description = """Dummy pipeline"""
+    nextflowVersion = '!>=23.04.0'
+    version = '9.9.9'
... (truncated)
hello-nf-core/solutions/core-hello-part2/subworkflows/nf-core/utils_nfschema_plugin/main.nf
View
@@ -2,30 +2,25 @@
 // Subworkflow that uses the nf-schema plugin to validate parameters and render the parameter summary
 //
 
-include { paramsSummaryLog   } from 'plugin/nf-schema'
+include { paramsSummaryLog } from 'plugin/nf-schema'
 include { validateParameters } from 'plugin/nf-schema'
-include { paramsHelp         } from 'plugin/nf-schema'
+include { paramsHelp } from 'plugin/nf-schema'
 
 workflow UTILS_NFSCHEMA_PLUGIN {
-
     take:
-    input_workflow      // workflow: the workflow object used by nf-schema to get metadata from the workflow
-    validate_params     // boolean:  validate the parameters
... (truncated)
hello-nf-core/solutions/core-hello-part2/workflows/hello.nf(truncated)
hello-nf-core/solutions/core-hello-part3/conf/base.config(truncated)
hello-nf-core/solutions/core-hello-part3/conf/modules.config(truncated)
hello-nf-core/solutions/core-hello-part3/conf/test.config(truncated)
hello-nf-core/solutions/core-hello-part3/conf/test_full.config(truncated)
hello-nf-core/solutions/core-hello-part3/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/modules/local/convertToUpper.nf(truncated)
hello-nf-core/solutions/core-hello-part3/modules/local/cowpy.nf(truncated)
hello-nf-core/solutions/core-hello-part3/modules/local/sayHello.nf(truncated)
hello-nf-core/solutions/core-hello-part3/modules/nf-core/cat/cat/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config(truncated)
hello-nf-core/solutions/core-hello-part3/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config(truncated)
hello-nf-core/solutions/core-hello-part3/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/local/utils_nfcore_hello_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/nf-core/utils_nextflow_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/nf-core/utils_nfcore_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part3/subworkflows/nf-core/utils_nfschema_plugin/main.nf(truncated)
hello-nf-core/solutions/core-hello-part3/workflows/hello.nf(truncated)
hello-nf-core/solutions/core-hello-part4/conf/base.config(truncated)
hello-nf-core/solutions/core-hello-part4/conf/modules.config(truncated)
hello-nf-core/solutions/core-hello-part4/conf/test.config(truncated)
hello-nf-core/solutions/core-hello-part4/conf/test_full.config(truncated)
hello-nf-core/solutions/core-hello-part4/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/local/convertToUpper.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/local/cowpy.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/local/cowpy/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/local/sayHello.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/nf-core/cat/cat/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config(truncated)
hello-nf-core/solutions/core-hello-part4/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config(truncated)
hello-nf-core/solutions/core-hello-part4/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/local/utils_nfcore_hello_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/nf-core/utils_nextflow_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/nf-core/utils_nfcore_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part4/subworkflows/nf-core/utils_nfschema_plugin/main.nf(truncated)
hello-nf-core/solutions/core-hello-part4/workflows/hello.nf(truncated)
hello-nf-core/solutions/core-hello-part5/conf/base.config(truncated)
hello-nf-core/solutions/core-hello-part5/conf/modules.config(truncated)
hello-nf-core/solutions/core-hello-part5/conf/test.config(truncated)
hello-nf-core/solutions/core-hello-part5/conf/test_full.config(truncated)
hello-nf-core/solutions/core-hello-part5/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/local/convertToUpper.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/local/cowpy.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/local/cowpy/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/local/sayHello.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/nf-core/cat/cat/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config(truncated)
hello-nf-core/solutions/core-hello-part5/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config(truncated)
hello-nf-core/solutions/core-hello-part5/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/local/utils_nfcore_hello_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/nf-core/utils_nextflow_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/nf-core/utils_nfcore_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-part5/subworkflows/nf-core/utils_nfschema_plugin/main.nf(truncated)
hello-nf-core/solutions/core-hello-part5/workflows/hello.nf(truncated)
hello-nf-core/solutions/core-hello-start/conf/base.config(truncated)
hello-nf-core/solutions/core-hello-start/conf/modules.config(truncated)
hello-nf-core/solutions/core-hello-start/conf/test.config(truncated)
hello-nf-core/solutions/core-hello-start/conf/test_full.config(truncated)
hello-nf-core/solutions/core-hello-start/main.nf(truncated)
hello-nf-core/solutions/core-hello-start/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/local/utils_nfcore_hello_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/nf-core/utils_nextflow_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/nf-core/utils_nfcore_pipeline/main.nf(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config(truncated)
hello-nf-core/solutions/core-hello-start/subworkflows/nf-core/utils_nfschema_plugin/main.nf(truncated)
hello-nf-core/solutions/core-hello-start/workflows/hello.nf(truncated)
side-quests/solutions/essential_scripting_patterns/collect.nf(truncated)
side-quests/solutions/essential_scripting_patterns/main.nf(truncated)
side-quests/solutions/essential_scripting_patterns/modules/fastp.nf(truncated)
side-quests/solutions/essential_scripting_patterns/modules/trimgalore.nf(truncated)
side-quests/solutions/metadata/1/main.nf(truncated)
side-quests/solutions/metadata/2/main.nf(truncated)
side-quests/solutions/metadata/3.2/main.nf(truncated)
side-quests/solutions/metadata/3.2/modules/cowpy.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/conf/base.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/conf/modules.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/conf/test.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/conf/test_full.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/modules/local/fastqe/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/modules/nf-core/multiqc/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/modules/nf-core/multiqc/tests/nextflow.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/modules/nf-core/seqtk/trim/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/nextflow.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/local/utils_nfcore_myfirstpipeline_pipeline/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/nf-core/utils_nextflow_pipeline/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/nf-core/utils_nfcore_pipeline/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/subworkflows/nf-core/utils_nfschema_plugin/main.nf(truncated)
side-quests/solutions/nf-core/myorg-myfirstpipeline/workflows/myfirstpipeline.nf(truncated)
side-quests/solutions/nf-test/tests/nextflow.config(truncated)
side-quests/solutions/plugin_development/main.nf(truncated)
side-quests/solutions/plugin_development/nextflow.config(truncated)
side-quests/solutions/splitting_and_grouping/main.nf(truncated)
side-quests/sol...*[Comment body truncated]*

pinin4fjords and others added 27 commits January 7, 2026 12:19
- Align markdown table columns
- Expand devcontainer JSON to multi-line format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Makefiles require tabs for recipe indentation (Make syntax requirement).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add new Section 1 covering plugin usage before development content
- Include examples of installing plugins via plugins {} block
- Show how to import and use plugin functions with include syntax
- Reference nf-schema as practical example (from Hello nf-core)
- Add table of popular community plugins
- Update learning goals to include plugin usage
- Renumber subsequent sections (1→2, 2→3, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The gradle-sdkman feature fails with SDKMAN permission errors during build.
Plugin projects include a Gradle wrapper (./gradlew) that downloads Gradle
automatically, so we only need Java pre-installed (provided by java:1 feature).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add prominent warning that this is an advanced topic
- Add info box explaining Java, Groovy, and Gradle for newcomers
- Highlight that "Using existing plugins" section is most important
- Add detailed breakdown of Groovy syntax with comparisons to Python
- Explain what unit tests are and why they matter
- Add troubleshooting tips for build failures
- Mark "Going further" section as reference material
- Add note that Gradle wrapper downloads Gradle automatically

These changes make the tutorial more accessible to participants
without a software engineering background.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The command requires both plugin name and organization name arguments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update tree output to show actual generated files
- Change package from nextflow.greeting to training.plugin
- Rename classes to NfGreetingExtension, NfGreetingPlugin, etc.
- Update all code examples and file paths to match
- Update test class names in examples and expected output
- Fix build.gradle example to show correct extensionPoints

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use correct plugin version (0.0.1-alpha4)
- Use correct nextflowVersion (24.10.0)
- Remove NfGreetingObserver from extensionPoints (not included by default)
- Add publishing block with registry configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show the actual generated template code and the changes needed,
making it clearer what participants need to modify.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The generated Makefile uses 'assemble' not 'build' for compilation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows the Gradle download on first run and sets expectations
for subsequent faster builds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reassure beginners that deprecation warnings are safe to ignore
and explain what each part of the output means.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Clarify that we're creating a new test file (not editing existing)
- Use Spock framework syntax to match generated project style
- Add note explaining Spock's given/expect syntax
- Update test output to show Spock-style test names

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Gradle hides test details on success, which can confuse beginners.
Added explanation that BUILD SUCCESSFUL means tests passed, with
optional --info flag for verbose output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The --info flag produces verbose Gradle output, not clean test results.
Instead, point users to the HTML test report for detailed results.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show how to use python http.server to view the Gradle test report
in VS Code, with screenshot showing all tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The starter files already have the plugin configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows the starter file and the changes needed to use plugin functions,
with highlighted lines and explanation of key changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The starter config has the plugin commented out. Users need to
uncomment it AND add the @0.1.0 version for local plugins.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added note explaining the startup/complete messages come from
the NfGreetingObserver included in the plugin template.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added section 1.6 with a practical exercise using nf-hello plugin.
Users create a simple workflow that uses the sayHello function,
seeing plugin download and usage in action.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added 'Why use plugins instead of local functions?' section
- Restructured exercise to show local function first, then plugin
- Before/After tabs show the code change clearly
- Explains the value proposition: versioned, shareable code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Connect trace observers to the messages users already saw
- Add Before/After exercise to customize observer messages
- Add table of available observer hooks
- Mark operators as reference (more complex)
- Update publishing section with clearer steps

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ion types

- Add progress table showing coverage of extension types from table 2.1
- Add TaskCounterObserver creation exercise (new file, not editing existing)
- Add custom operator (shoutAll) with full Before/After convention
- Add configurable decorator exercise with Before/After
- Add conceptual coverage of Factories, Executors, and Filesystems
- Update publishing section with complete instructions
- Clean up solution main.nf formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix duplicate 5.4 numbering (install locally → 5.5)
- Split monolithic section 7 into focused sections:
  - Section 7: Trace observers
  - Section 8: Custom operators and factories
  - Section 9: Configuration
  - Section 10: Publishing your plugin
- Add Takeaway/What's next transitions between new sections

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
pinin4fjords and others added 30 commits January 9, 2026 13:05
- Replace implicit 'it' with explicit parameter in summary example
- Update section references from 3-10 to 3-11 throughout
- Add operators and trace observers to development checklist
- Change "publish" to "distribute" in learning goals

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix hl_lines to highlight correct lines (24-29 for imports, 62-73 for operator)
- Add line-by-line breakdown explaining DataflowHelper, callbacks, and Channel.STOP

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix malformed hl_lines (24-16 -> 24-26)
- Add key changes explanation for TaskCounterObserver (verbose flag, constructor, conditional print)
- Add explanation for NfGreetingFactory config reading

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-io/training into custom-training-advanced-topics
- Convert section 0's #### headings to numbered ### subsections (0.1-0.6)
  to match the style used throughout the rest of the document
- Use consistent dot notation for taskCounter.verbose in section 9.2
  to match section 9.3's style

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Improved the explanation to acknowledge that publishDir can be configured
via processes or config selectors (withName/withLabel), and explained
what a manifest is for readers unfamiliar with the term.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Explains the fundamental shift: with publishDir, processes handle their
own publishing; with workflow outputs, publishing moves to the workflow
level. This provides context before diving into syntax details.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- CLAUDE.md: Add detailed explanation that hl_lines is snippet-relative,
  with worked example showing how to count lines (including blanks)
- new-lesson.md: Add concise instruction for setting hl_lines correctly
- New check-highlights skill: Verification tool for reviewing highlight
  line numbers in existing documentation

Key points reinforced: count from line 1 of snippet, blank lines count,
hl_lines is independent of linenums.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Section 1 now starts with processes that already have metadata tuples
  (greeting and language), so adding tuples isn't conflated with the
  workflow output syntax
- Section 2 now only teaches removing publishDir and adding the workflow
  output syntax - a single focused concept
- Updated starter files (main.nf, modules/greetings.nf) to match docs
- Fixed hl_lines values that were off-by-one

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a workflow uses a publish: section, the workflow content must be
placed in a main: block. Updated the After example and explanation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows the complete main.nf file with and without the output block,
making the change clearer and more consistent with other sections.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The training environment uses an older Nextflow version that requires
this flag. Added note explaining this is not needed in 25.10+.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The solution was missing the required main: block when using publish:

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The nextflow.preview.output flag must be in the script file, not
nextflow.config. Updated documentation and solution to add the flag
at the top of main.nf when the output block is added.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Include nextflow.preview.output in the starter main.nf so users don't
need to add it manually. Updated all code blocks in documentation to
include the flag consistently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- CSV output has no header row (tuples don't have field names)
- Paths are absolute, not relative
- Clarified tip about using maps for named fields

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Refactored to use nf-core community standard [meta, file] tuples
- meta map contains id and language fields
- Updated all code examples to use meta.language for dynamic paths
- Changed index format from CSV to JSON (works better with meta maps)
- JSON properly expands meta maps to objects with named fields
- Removed outdated tip about using maps (now using them from the start)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- JSON index shows all 5 greetings with proper /workspaces paths
- File listing sorted alphabetically to match real output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Line 25 was the process declaration, not the second publishDir which
is on line 27.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Based on feedback from @bentsherman: custom operators/factories are in a
precarious position for future type checking support, and nearly every
example would have been better as a custom function.

Changes:
- Remove Section 8 (Custom operators and factories) entirely
- Remove Section 10.1 (Channel factories)
- Renumber sections 9-11 to 8-10
- Update all references to operators/factories throughout
- Update code examples to use functions instead of shoutAll operator
- Keep focus on functions and trace observers as the key extension types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove shoutAll operator from main.nf solution, use reverseGreeting
function instead to match the updated tutorial content.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change "functions, factories, etc." to "functions, observers, etc." to
avoid confusion with channel factories (which were removed).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants