Agent Skill · cast

summarize-nextflow

Read a Nextflow pipeline source tree (nf-core or ad-hoc DSL2) and emit a structured JSON summary for downstream translation Molds.

← All cast skills · Source mold →

Install with Claude Code

/plugin marketplace add galaxyproject/foundry
/plugin install foundry-skills@galaxy-workflow-foundry

Then invoke as:

/foundry-skills:summarize-nextflow

Install with Codex

codex plugin marketplace add galaxyproject/foundry
codex plugin add foundry-skills@galaxy-workflow-foundry

Then select with /skills or invoke explicitly as:

$summarize-nextflow

Install with Antigravity

git clone https://github.com/galaxyproject/foundry
agy plugin install foundry/casts/claude

Then select with /skills or invoke explicitly as:

/summarize-nextflow

Skill Bundle

/ packaged cast
attached files
11
upfront
5
on demand
6
cast rev
11
validated
1

Produces: 1 artifact.

Open questions: 1 recorded in provenance.

Artifact Contract

/ skill handoff

Produces

summary-nextflow

A structured JSON summary of a Nextflow pipeline, including its interface, processes, data flow, software environment, and test fixtures.

jsonsummary-nextflow.json[[summary-nextflow]]
Raw artifact contract
{
  "id": "summary-nextflow",
  "kind": "json",
  "default_filename": "summary-nextflow.json",
  "schema": "[[summary-nextflow]]",
  "description": "A structured JSON summary of a Nextflow pipeline, including its interface, processes, data flow, software environment, and test fixtures."
}

Validation

/ cast checks
summary-nextflow

validate-summary-nextflow

passed
validator
validate-summary-nextflow
path
casts/claude/skills/summarize-nextflow/runs/nf-core__demo/summary.json
exit
0
artifact hash
9193bc9b6f94ac57d04ccbb72047f94eaf612a8c736297f84d5e37685c175208
Raw validation result
{
  "artifact_id": "summary-nextflow",
  "path": "casts/claude/skills/summarize-nextflow/runs/nf-core__demo/summary.json",
  "validator_bin": "validate-summary-nextflow",
  "status": "passed",
  "exit_code": 0,
  "artifact_hash": "9193bc9b6f94ac57d04ccbb72047f94eaf612a8c736297f84d5e37685c175208",
  "stdout": "casts/claude/skills/summarize-nextflow/runs/nf-core__demo/summary.json: valid\n",
  "stderr": "",
  "stdout_hash": "6a46379bfea97b008c65073ec062ee9d06013867110e3a53a0551ba6e0b67d3d",
  "stderr_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}

Attached Files

/ runtime references

Load upfront

cli-tool

foundry

packaged

Schema-check summary-nextflow.json before returning it from the skill.

upfront runtime verbatim cast-validated deterministic 937 B
bundle
references/cli/foundry.md
source
content/cli/foundry/index.md
Preview md
---
type: cli-tool
tool: foundry
origin: npm
package: "@galaxy-foundry/gxwf-foundry"
invoke: foundry
invoke_fallback: "npx --package @galaxy-foundry/gxwf-foundry foundry"
availability_check: "foundry --help"
docs_url: "https://github.com/galaxyproject/foundry/blob/main/packages/gxwf-foundry/README.md"
tags:
  - cli/foundry
status: draft
created: 2026-05-11
revised: 2026-05-11
revision: 1
summary: "Foundry CLI: bundles all Mold IO validators and a summarize-nextflow subcommand."
---

# foundry

Unified Foundry CLI. Subcommands cover every Mold IO validator plus a `summarize-nextflow` wrapper around the standalone `@galaxy-foundry/summarize-nextflow` package. Per-subcommand synopsis, args, and options are rendered from `@galaxy-foundry/gxwf-foundry/meta`.

## Install

`npx --package @galaxy-foundry/gxwf-foundry foundry <subcommand>` runs without a global install. For repeat use, `npm install -g @galaxy-foundry/gxwf-foundry`.
schema

nextflow-parameters-meta

packaged

Validate per-pipeline nextflow_schema.json (Draft 2020-12) when extracting params[].

upfront both verbatim corpus-observed deterministic 4.2 KB
bundle
references/schemas/nextflow-parameters-meta.schema.json
source
package://@galaxy-foundry/summarize-nextflow#nextflowParametersMetaSchema
Preview json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://nextflow.io",
  "title": "Nextflow Schema Meta-schema",
  "description": "Meta-schema to validate Nextflow parameter schema files",
  "type": "object",
  "properties": {
    "$schema": {
      "title": "schema",
      "type": "string",
      "minLength": 1
    },
    "$id": {
      "title": "ID URI",
      "type": "string",
      "minLength": 1
    },
    "title": {
      "title": "Title",
      "type": "string",
      "minLength": 1
    },
    "description": {
      "title": "Description",
      "type": "string",
      "minLength": 1
    },
    "type": {
      "title": "Top level type",
      "type": "string",
      "const": "object"
    },
    "$defs": {
      "title": "Parameter groups",
      "type": "object",
      "patternProperties": {
        "^.*$": {
          "type": "object",
          "required": [
            "title",
            "type",
            "properties"
          ],
          "properties": {
            "title": {
              "type": "string",
              "minLength": 1
            },
            "type": {
              "const": "object"
            },
            "fa_icon": {
              "type": "string",
              "pattern": "^fa"
            },
            "description": {
              "type": "string"
            },
            "required": {
              "type": "array"
            },
            "properties": {
              "type": "object",
              "patternProperties": {
                "^.*$": {
                  "type": "object",
                  "required": [
                    "type"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "enum": [
                        "string",
                        "boolean",
                        "integer",
                        "number"
                      ]
                    },
                    "format": {
                      "type": "string",
                      "enum": [
                        "file-path",
                        "directory-path",
                        "path",
                        "file-path-pattern"
                      ]
                    },
                    "exists": {
                      "type": "boolean"
                    },
              
...
schema

nf-core-module-meta

packaged

Validate per-module meta.yml when walking nf-core modules; pins the channel IO `type` enum and tools/containers shape.

upfront both verbatim corpus-observed deterministic 8.8 KB
bundle
references/schemas/nf-core-module-meta.schema.json
source
package://@galaxy-foundry/summarize-nextflow#nfCoreModuleMetaSchema
Preview json
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "title": "Meta yaml",
  "description": "Validate the meta yaml file for an nf-core module",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Name of the module"
    },
    "description": {
      "type": "string",
      "description": "Description of the module"
    },
    "keywords": {
      "type": "array",
      "description": "Keywords for the module",
      "items": {
        "type": "string",
        "not": {
          "const": "example"
        }
      },
      "uniqueItems": true,
      "minItems": 3
    },
    "tools": {
      "type": "array",
      "description": "Tools used by the module",
      "items": {
        "type": "object",
        "patternProperties": {
          ".*": {
            "type": "object",
            "properties": {
              "description": {
                "type": "string",
                "description": "Description of the output channel"
              },
              "homepage": {
                "type": "string",
                "description": "Homepage of the tool",
                "pattern": "^https?://.*$"
              },
              "documentation": {
                "type": "string",
                "description": "Documentation of the tool",
                "pattern": "^(https?|ftp)://.*$"
              },
              "tool_dev_url": {
                "type": "string",
                "description": "URL of the development version of the tool's documentation",
                "pattern": "^https?://.*$"
              },
              "doi": {
                "description": "DOI of the tool",
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^10\\.\\d{4,9}\\/[^,]+$"
                  },
                  {
                    "type": "string",
                    "enum": [
                      "no DOI available"
                    ]
                  }
                ]
              },
              "licence": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Licence of the tool",
                "minItems": 1,
                "uniqueItems": true,
                "message": "Licence must be an array of one or more entries, e.g. [\"MIT\"]"
...
schema

nf-core-subworkflow-meta

packaged

Validate subworkflow meta.yml; backs Subworkflow.calls extraction via the components: declaration.

upfront both verbatim corpus-observed deterministic 3.9 KB
bundle
references/schemas/nf-core-subworkflow-meta.schema.json
source
package://@galaxy-foundry/summarize-nextflow#nfCoreSubworkflowMetaSchema
Preview json
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "title": "Meta yaml",
  "description": "Validate the meta yaml file for an nf-core subworkflow",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Name of the subworkflow"
    },
    "description": {
      "type": "string",
      "description": "Description of the subworkflow"
    },
    "authors": {
      "type": "array",
      "description": "Authors of the subworkflow",
      "items": {
        "type": "string"
      }
    },
    "maintainers": {
      "type": "array",
      "description": "Maintainers of the subworkflow",
      "items": {
        "type": "string"
      }
    },
    "components": {
      "type": "array",
      "description": "Modules and subworkflows used in the subworkflow",
      "items": {
        "type": "string"
      },
      "minItems": 0
    },
    "keywords": {
      "type": "array",
      "description": "Keywords for the module",
      "items": {
        "type": "string"
      },
      "minItems": 3
    },
    "input": {
      "type": "array",
      "description": "Input channels for the subworkflow",
      "items": {
        "type": "object",
        "patternProperties": {
          ".*": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "Type of the input channel"
              },
              "description": {
                "type": "string",
                "description": "Description of the input channel"
              },
              "pattern": {
                "type": "string",
                "description": "Pattern of the input channel, given in Java glob syntax"
              },
              "default": {
                "type": [
                  "string",
                  "number",
                  "boolean",
                  "array",
                  "object"
                ],
                "description": "Default value for the input channel"
              },
              "enum": {
                "type": "array",
                "description": "List of allowed values for the input channel",
                "items": {
                  "type": [
                    "string",
                    "number",
                    "boolean",
                    "array",
                    "object"
          
...
schema

summary-nextflow

packaged

Validate the emitted Nextflow summary JSON and provide downstream consumers the output contract.

upfront both verbatim cast-validated deterministic 69.9 KB
bundle
references/schemas/summary-nextflow.schema.json
source
package://@galaxy-foundry/summarize-nextflow#summaryNextflowSchema
Preview json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://galaxyproject.org/foundry/schemas/summary-nextflow.schema.json",
  "$comment": "Canonical source: packages/summarize-nextflow/src/schema/summary-nextflow.schema.json in galaxyproject/foundry. Mold frontmatter cites this schema via [[summary-nextflow]] wiki-links; the cast pipeline imports the `summaryNextflowSchema` runtime export and serializes it into cast bundles.",
  "title": "Nextflow Pipeline Summary",
  "description": "Structured per-source summary emitted by the summarize-nextflow Mold.\n\nPer-source schema by design — paper, Nextflow, and CWL each have their own summary shape; downstream Molds (data flow, templates, tool wrappers) consume any source's summary and handle the polymorphism.\n\nField names mirror gxy-sketches' SketchSource / ToolSpec / TestDataRef / ExpectedOutputRef where parity exists; see content/research/gxy-sketches-alignment/index.md.",
  "$ref": "#/$defs/Summary",
  "$defs": {
    "Summary": {
      "title": "Summary",
      "description": "Top-level shape. Every Nextflow summary is exactly this object.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "source",
        "params",
        "sample_sheets",
        "profiles",
        "tools",
        "processes",
        "subworkflows",
        "workflow",
        "reference_assets",
        "reference_rebuilds",
        "test_candidates",
        "test_selection"
      ],
      "properties": {
        "source": {
          "$ref": "#/$defs/SourceRecord"
        },
        "params": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Param"
          }
        },
        "sample_sheets": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SampleSheet"
          },
          "description": "Structured sample-sheet inputs. Each entry binds one `params[]` parameter to a row schema (column names, types, path-vs-meta classification, required flags, enums, patterns). Promoted from prose inside `params[].description` so downstream target translations (Galaxy `sample_sheet*` collections, CWL records-of-arrays) can choose collection variants without re-parsing the source pipeline. Empty array when no sample-sheet idiom is detected. Discovery sources: nf-schema `schema:` references, `samplesheetToList()` calls, and `splitCs
...

Load on demand

research

component-nextflow-containers-and-envs

packaged

Resolve container, conda, Wave, and Bioconda/Biocontainers environment evidence.

Trigger: When extracting tools, versions, containers, conda directives, or environment equivalences.

on-demand runtime verbatim corpus-observed deterministic 31.1 KB
bundle
references/notes/component-nextflow-containers-and-envs.md
source
content/research/component-nextflow-containers-and-envs/index.md
Preview md
---
type: research
tags:
  - source/nextflow
  - target/galaxy
component: "Nextflow Containers and Environments"
status: draft
created: 2026-05-01
revised: 2026-05-05
revision: 3
summary: "Container URL grammar (depot, BioContainers, mulled-v2, Wave, ORAS) and conda directive resolution rules backing summarize-nextflow §5."
companions:
  - "component-nextflow-containers-and-envs.yml"
sources:
  - "https://docs.seqera.io/nextflow/process"
  - "https://docs.seqera.io/nextflow/reference/process"
  - "https://github.com/nf-core/modules/blob/master/modules/nf-core/fastqc/main.nf"
  - "https://github.com/nf-core/modules/blob/master/modules/nf-core/multiqc/main.nf"
  - "https://github.com/nf-core/modules/blob/master/modules/nf-core/dragmap/align/main.nf"
  - "https://github.com/nf-core/modules/blob/master/modules/nf-core/seqkit/sample/main.nf"
  - "https://github.com/nf-core/modules/blob/master/modules/meta-schema.json"
  - "https://github.com/nf-core/modules/blob/master/modules/environment-schema.json"
  - "https://github.com/nf-core/tools/blob/master/nf_core/module-template/main.nf"
  - "https://github.com/BioContainers/multi-package-containers"
  - "https://github.com/BioContainers/singularity-build-bot"
  - "https://depot.galaxyproject.org/singularity/"
  - "https://biocontainers.pro/registry"
  - "https://bioconda.github.io/"
  - "https://docs.seqera.io/wave"
  - "https://nf-co.re/events/2024/bytesize_pipeline_container_urls"
related_molds:
  - "[[summarize-nextflow]]"
  - "[[author-galaxy-tool-wrapper]]"
  - "[[summarize-galaxy-tool]]"
related_notes:
  - "[[component-nextflow-pipeline-anatomy]]"
  - "[[component-nf-core-tools]]"
  - "[[component-nextflow-inspect]]"
---

# Nextflow Containers and Environments

Operational grounding for [[summarize-nextflow]] §5 ("Build th
...
research

component-nextflow-containers-and-envs

packaged

Resolve container, conda, Wave, and Bioconda/Biocontainers environment evidence.

Trigger: When extracting tools, versions, containers, conda directives, or environment equivalences.

on-demand runtime verbatim corpus-observed deterministic 7.1 KB
bundle
references/notes/component-nextflow-containers-and-envs.yml
source
content/research/component-nextflow-containers-and-envs/component-nextflow-containers-and-envs.yml
Preview yml
# Container & conda directive cheatsheet for summarize-nextflow §5.
# Companion to component-nextflow-containers-and-envs.md.
#
# Matching contract: regexes match URL/string token *content* after the cast skill
# has unwrapped the directive's outer "${ ... ? '<url>' : '<url>' }" GString.
# The walker is responsible for extracting both ternary-branch strings before
# applying these patterns.
#
# Bucketing rule: by URL prefix (NOT by ternary branch). See note §"Bucketing rule".

ternary_predicate:
  description: "Detect either form of the canonical nf-core ternary predicate."
  patterns:
    - "workflow\\.containerEngine\\s*==\\s*'singularity'"
    - "workflow\\.containerEngine\\s+in\\s*\\[\\s*'singularity'(?:\\s*,\\s*'apptainer')?\\s*\\]"
  also_typical: "&& !task.ext.singularity_pull_docker_container"

container_forms:

  - id: galaxy_singularity_simple
    regex: "^https://depot\\.galaxyproject\\.org/singularity/(?P<name>[^:/]+):(?P<version>[^-][^-]*)--(?P<build>[^/]+)$"
    example: "https://depot.galaxyproject.org/singularity/fastqc:0.12.1--hdfd78af_0"
    source: "modules/nf-core/fastqc/main.nf"
    resolves_to_field: "singularity"
    derivation_rule: "name+version+build from path basename; Bioconda dual exists at quay.io/biocontainers/<name>:<version>--<build>."
    notes: "BioContainers Singularity mirror; CVMFS-distributed; produced by singularity-build-bot from quay.io."

  - id: galaxy_singularity_mulled
    regex: "^https://depot\\.galaxyproject\\.org/singularity/mulled-v2-(?P<hash>[0-9a-f]+):(?P<verhash>[0-9a-f]+)-(?P<rev>\\d+)$"
    example: "https://depot.galaxyproject.org/singularity/mulled-v2-580d344d9d4a496cd403932da8765f9e0187774d:df80ed8d23d0a2c43181a2b3dd1b39f2d00fab5c-0"
    source: "modules/nf-core/dragmap/align/main.nf"
    resolves_to_field: "sin
...
research

component-nextflow-pipeline-anatomy

packaged

Interpret DSL2 layout, includes, workflow/subworkflow/module boundaries, and channel/process topology.

Trigger: When walking pipeline structure or resolving process aliases and channel flow.

on-demand runtime verbatim hypothesis deterministic 967 B
bundle
references/notes/component-nextflow-pipeline-anatomy.md
source
content/research/component-nextflow-pipeline-anatomy/index.md
Preview md
---
type: research
tags:
  - source/nextflow
component: "Nextflow Pipeline Anatomy (DSL2)"
status: draft
created: 2026-05-01
revised: 2026-05-01
revision: 1
related_notes:
  - "[[nextflow-workflow-io-semantics]]"
  - "[[iwc-map-over-lifecycle-survey]]"
  - "[[iwc-parameter-derivation-survey]]"
summary: "Stub. DSL2 layout, channel idioms, operator-chain reading rules. Grows from cast contact with rnaseq/sarek/ad-hoc — see issue #17."
related_molds:
  - "[[summarize-nextflow]]"
---

# Nextflow Pipeline Anatomy (DSL2)

Stub. Grown from cast contact, not pre-emptively (see issue #17).

## Primary sources

- Nextflow DSL2 docs: https://www.nextflow.io/docs/latest/dsl2.html
- nf-core pipeline structure: https://nf-co.re/docs/contributing/pipelines
- nf-core module spec: https://nf-co.re/docs/contributing/modules

## Open gaps

_Paragraphs land here when the runtime cast hits a pipeline shape SKILL.md does not cover. Each entry names the motivating target._
research

component-nextflow-testing

packaged

Extract nf-test files, snapshot fixtures, test profiles, and Nextflow test-data conventions.

Trigger: When filling test_candidates and test_selection sections of the summary.

on-demand runtime verbatim hypothesis deterministic 5.6 KB
bundle
references/notes/component-nextflow-testing.md
source
content/research/component-nextflow-testing/index.md
Preview md
---
type: research
tags:
  - source/nextflow
component: "Nextflow Testing and Test Fixtures"
status: draft
created: 2026-05-01
revised: 2026-09-15
revision: 5
summary: "nf-test patterns mapped to Galaxy planemo asserts and CWL test equivalents — backs the nextflow test-plan Molds and summarize-nextflow §7."
companions:
  - "component-nextflow-testing.yml"
sources:
  - "https://www.nf-test.com/"
  - "https://www.nf-test.com/docs/assertions/"
  - "https://www.nf-test.com/docs/assertions/snapshots/"
  - "https://www.nf-test.com/docs/configuration/"
  - "https://nf-co.re/docs/contributing/nf-test/assertions"
  - "https://nf-co.re/docs/developing/testing/overview"
  - "https://github.com/nf-core/test-datasets"
  - "https://www.nextflow.io/docs/latest/config.html#config-profiles"
  - "https://nf-co.re/docs/contributing/pipelines#test-data"
related_molds:
  - "[[summarize-nextflow]]"
  - "[[nextflow-test-to-galaxy-test-plan]]"
  - "[[nextflow-test-to-cwl-test-plan]]"
  - "[[implement-galaxy-workflow-test]]"
related_notes:
  - "[[nextflow-test-case-selection]]"
  - "[[planemo-asserts-idioms]]"
  - "[[tests-format]]"
  - "[[iwc-test-data-conventions]]"
  - "[[component-nf-core-tools]]"
---

# Nextflow Testing and Test Fixtures

Operational grounding for three Molds:

- [[summarize-nextflow]] §7 — enumerate whole-pipeline `test_candidates[]` and make the default `test_selection` from a real nf-core or DSL2 pipeline.
- [[nextflow-test-to-galaxy-test-plan]] — translate nf-test fixtures + assertions into Galaxy equivalents.
- [[nextflow-test-to-cwl-test-plan]] — the same translation, toward CWL.

The summarize side is mostly *enumeration*: walk `tests/*.nf.test`, extract structured fields per the Mold §7 spec. The translation side is *mapping*: each nf-test assertion pattern has a (
...
research

component-nextflow-testing

packaged

Extract nf-test files, snapshot fixtures, test profiles, and Nextflow test-data conventions.

Trigger: When filling test_candidates and test_selection sections of the summary.

on-demand runtime verbatim hypothesis deterministic 11.6 KB
bundle
references/notes/component-nextflow-testing.yml
source
content/research/component-nextflow-testing/component-nextflow-testing.yml
Preview yml
# nf-test patterns → Galaxy / CWL test equivalents.
# Companion to component-nextflow-testing.md.
#
# Rows ordered by frequency in real nf-core pipelines (snapshot block first).

mappings:

  # ---- Snapshot block (nf-core canonical idiom) ----

  - id: snapshot.match
    nf_test_pattern: "assert snapshot(...).match()"
    description: "Top-level snapshot assertion comparing serialized form of N captured values to a stored .nf.test.snap file."
    galaxy_equivalent: "Per-output assertion block in <workflow>-tests.yml; no single-line analog. Decompose into per-capture assertions (rows below)."
    cwl_equivalent: "cwltest expected_outputs entries per output; no single-line analog. Decompose."
    target_link: "[[tests-format]]"
    notes: "The whole-snapshot idiom is lossy on translation; explode into N per-capture assertions and accept the surface-area increase."

  - id: snapshot.succeeded_task_count
    nf_test_pattern: "workflow.trace.succeeded().size()"
    description: "Count of successfully completed tasks. nf-core canonical first capture; brittle across pipeline versions but stable per release."
    galaxy_equivalent: "Implicit in workflow execution success. Galaxy workflow tests pass iff the workflow completes; per-step success surfaces via has_text on log outputs."
    cwl_equivalent: "Implicit in cwltest's `should_succeed` / `should_fail` boolean. No per-task count."
    target_link: "[[planemo-asserts-idioms]] §1 (Plain text reports / logs)"
    notes: "Drop on translation; targets verify success implicitly."

  - id: snapshot.versions_yml
    nf_test_pattern: "ch_versions or path('versions.yml')"
    description: "The `versions` topic channel collected per process and dumped to versions.yml at workflow end. nf-core canonical second capture."
    galaxy_equiv
...
research

nextflow-test-case-selection

packaged

Choose the first whole-pipeline test candidate without equating a profile name with runnability or scientific coverage.

Trigger: When no explicit profile was supplied, multiple pipeline-level nf-tests exist, or the apparent candidate is full-scale, minimal, tiny, or stub-only.

on-demand runtime verbatim corpus-observed deterministic 9.1 KB
bundle
references/notes/nextflow-test-case-selection.md
source
content/research/nextflow-test-case-selection/index.md
Preview md
---
type: research
title: "Selecting the first Nextflow test case"
tags:
  - source/nextflow
status: draft
created: 2026-09-15
revised: 2026-09-15
revision: 2
summary: "A deterministic policy for choosing the first whole-pipeline Nextflow test case without mistaking profile names for coverage."
sources:
  - "https://github.com/galaxyproject/foundry/issues/67"
  - "https://www.nf-test.com/docs/configuration/"
  - "https://www.nf-test.com/docs/cli/list/"
  - "https://www.nextflow.io/docs/latest/config.html#config-profiles"
related_molds:
  - "[[summarize-nextflow]]"
  - "[[nextflow-test-to-galaxy-test-plan]]"
  - "[[nextflow-test-to-cwl-test-plan]]"
related_notes:
  - "[[component-nextflow-testing]]"
  - "[[component-nextflow-inspect]]"
  - "[[open-requirements-ledger]]"
---

# Selecting the first Nextflow test case

The first translated test should be small enough to run during workflow construction while still exercising the scientific path the translated workflow claims to implement. Profile names help find candidates, but they do not establish either property.

This note owns **selection**. [[component-nextflow-testing]] owns fixture and assertion interpretation after cases have been enumerated. [[summarize-nextflow]] owns applying the selection policy and emits both the candidate set and the decision. The two Nextflow test-plan Molds consume that decision; they must not silently choose a different case. [[run-workflow-test]] is downstream of translation and sees a Galaxy or CWL test artifact, not a Nextflow profile, so it does not use this note.

## Unit of selection

A candidate is a whole-pipeline execution, not merely a profile name:

- for nf-test, one `test(...)` case inside a `nextflow_pipeline { ... }` suite, identified by file path and test name;
- otherwise,
...

SKILL.md


# summarize-nextflow

Follow the procedure below and use the artifact/reference sections as the runtime contract.

## When To Use

- Read a Nextflow pipeline source tree (nf-core or ad-hoc DSL2) and emit a structured JSON summary for downstream translation Molds.

## Inputs

- No upstream artifact inputs declared. See the procedure for user-supplied runtime inputs.

## Outputs

- Write artifact `summary-nextflow` as `summary-nextflow.json`. Format: `json`. Schema: summary-nextflow. A structured JSON summary of a Nextflow pipeline, including its interface, processes, data flow, software environment, and test fixtures.

## Required Tools

- **`foundry`** (foundry). `npm install -g @galaxy-foundry/gxwf-foundry`.
  Ephemeral run: `npx --package @galaxy-foundry/gxwf-foundry foundry`.
  Check: `foundry --help`.
  Docs: https://github.com/galaxyproject/foundry/blob/main/packages/gxwf-foundry/README.md
  Bundled reference: `references/cli/foundry.md`.

## Load Upfront

- `references/cli/foundry.md`: CLI tool reference copied verbatim into the bundle. Schema-check summary-nextflow.json before returning it from the skill.
- `references/schemas/nextflow-parameters-meta.schema.json`: Schema file copied verbatim into the bundle. Validate per-pipeline nextflow_schema.json (Draft 2020-12) when extracting params[].
- `references/schemas/nf-core-module-meta.schema.json`: Schema file copied verbatim into the bundle. Validate per-module meta.yml when walking nf-core modules; pins the channel IO `type` enum and tools/containers shape.
- `references/schemas/nf-core-subworkflow-meta.schema.json`: Schema file copied verbatim into the bundle. Validate subworkflow meta.yml; backs Subworkflow.calls extraction via the components: declaration.
- `references/schemas/summary-nextflow.schema.json`: Schema file copied verbatim into the bundle. Validate the emitted Nextflow summary JSON and provide downstream consumers the output contract.

## Load On Demand

- `references/notes/component-nextflow-containers-and-envs.md`: Research note copied verbatim into the bundle. Resolve container, conda, Wave, and Bioconda/Biocontainers environment evidence. Use when: extracting tools, versions, containers, conda directives, or environment equivalences.
- `references/notes/component-nextflow-containers-and-envs.yml`: Companion file copied verbatim into the bundle. Sibling of `references/notes/component-nextflow-containers-and-envs.md`; read it where that note directs.
- `references/notes/component-nextflow-pipeline-anatomy.md`: Research note copied verbatim into the bundle. Interpret DSL2 layout, includes, workflow/subworkflow/module boundaries, and channel/process topology. Use when: walking pipeline structure or resolving process aliases and channel flow.
- `references/notes/component-nextflow-testing.md`: Research note copied verbatim into the bundle. Extract nf-test files, snapshot fixtures, test profiles, and Nextflow test-data conventions. Use when: filling test_candidates and test_selection sections of the summary.
- `references/notes/component-nextflow-testing.yml`: Companion file copied verbatim into the bundle. Sibling of `references/notes/component-nextflow-testing.md`; read it where that note directs.
- `references/notes/nextflow-test-case-selection.md`: Research note copied verbatim into the bundle. Choose the first whole-pipeline test candidate without equating a profile name with runnability or scientific coverage. Use when: no explicit profile was supplied, multiple pipeline-level nf-tests exist, or the apparent candidate is full-scale, minimal, tiny, or stub-only.

## Validation

- Validate `summary-nextflow.json` before returning it: run `foundry validate-summary-nextflow summary-nextflow.json` from `@galaxy-foundry/gxwf-foundry`. If the command is not on PATH, run `npx --package @galaxy-foundry/gxwf-foundry foundry validate-summary-nextflow summary-nextflow.json`. This checks artifact `summary-nextflow` against the summary-nextflow schema.

## Procedure

Read a Nextflow pipeline source tree (nf-core or ad-hoc DSL2) and emit a structured JSON summary describing its processes, channels, conditionals, containers, parameters, and test fixtures. Source-specific (Nextflow), target-agnostic. The summary is the input to every downstream skill in the `NEXTFLOW → GALAXY` and `NEXTFLOW → CWL` pipelines: `nextflow-summary-to-galaxy-interface`, `nextflow-summary-to-galaxy-data-flow`, `nextflow-summary-to-cwl-interface`, `nextflow-summary-to-cwl-data-flow`, `author-galaxy-tool-wrapper` (for the container/conda block), `nextflow-test-to-galaxy-test-plan`, and `nextflow-test-to-cwl-test-plan` (for the test-fixture block).

This skill owns **only the read-and-structure step**. Every cross-source-and-target translation lives downstream; this skill is responsible for surfacing what exists in the NF tree honestly, not for reshaping it toward Galaxy or CWL idioms.

The output schema is per-source by design — see gxy-sketches-alignment for why a forced-shared cross-source summary shape was rejected.

### Inputs

The skill expects:

- A **path or git URL** to the NF pipeline. Local clone is preferred; a git URL triggers a shallow clone the skill manages.
- Optional **pin**: tag, branch, or commit SHA. Mirrors `SketchSource` semantics from gxy-sketches.
- Optional explicit **profile override** (`test`, `test_full`, …) applied while resolving each candidate's effective profile chain. It does not identify a test case. When omitted, there is no silent profile default.
- Optional **test-data directory**. When provided with fixture fetching, remote samplesheets and referenced files are downloaded under that directory and their local paths are recorded in each candidate's `inputs[].path`.

Whole-pipeline only. The skill does **not** accept "summarize this single subworkflow" subset hints; subset summarization is an open question — see Non-goals.

### Outputs

A single JSON document conforming to summary-nextflow (`packages/summarize-nextflow/src/schema/summary-nextflow.schema.json`). Sketch shape:

```jsonc
{
  "source": {                                  // mirrors SketchSource
    "ecosystem": "nf-core" | "nextflow",
    "workflow": "rnaseq",
    "url": "https://github.com/nf-core/rnaseq",
    "version": "3.14.0",                       // tag or commit SHA
    "license": "MIT",
    "slug": "nf-core-rnaseq"
  },
  "params": [
    { "name": "input", "type": "path", "default": null,
      "description": "Samplesheet CSV", "required": true }
  ],
  "sample_sheets": [
    { "param": "input",
      "schema_path": "assets/schema_input.json",
      "discovered_via": "nf-schema",
      "format": "csv", "header": true,
      "columns": [
        { "name": "sample",     "type": "string", "kind": "meta", "required": true,
          "pattern": "^\\S+$" },
        { "name": "fastq_1",    "type": "string", "kind": "data", "format": "file-path",
          "required": true,  "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$" },
        { "name": "fastq_2",    "type": "string", "kind": "data", "format": "file-path",
          "required": false, "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$" },
        { "name": "strandedness","type": "string", "kind": "meta", "required": true,
          "enum": ["forward", "reverse", "unstranded", "auto"] }
      ] }
  ],
  "profiles": [                                // classified from the body, never the name
    { "name": "test",      "kinds": ["test"],      "source_path": "nextflow.config",
      "includes": ["conf/test.config"],
      "signals": ["include:test-config", "sets-input-data", "params-assignment"] },
    { "name": "test_full", "kinds": ["test"],      "source_path": "nextflow.config",
      "includes": ["conf/test_full.config"],
      "signals": ["include:test-config", "sets-input-data", "params-assignment"] },
    { "name": "docker",    "kinds": ["container"], "source_path": "nextflow.config",
      "includes": [], "signals": ["container-keys"] },
    { "name": "debug",     "kinds": ["dev"],       "source_path": "nextflow.config",
      "includes": [], "signals": ["dev-profile-name"] }
  ],
  "tools": [                                   // mirrors gxy-sketches ToolSpec, augmented
    { "name": "fastp", "version": "0.23.4",
      "biocontainer": "biocontainers/fastp:0.23.4--h5f740d0_0",   // accepts quay.io/ or docker.io biocontainers/ alias
      "bioconda":     "bioconda::fastp=0.23.4",
      "docker":       null,
      "singularity":  "https://depot.galaxyproject.org/singularity/fastp:0.23.4--h5f740d0_0",
      "wave":         null }                                        // Seqera Wave / community-cr registry
  ],
  "processes": [
    { "name": "MINIMAP2_ALIGN",                               // canonical name
      "aliases": ["MINIMAP2_CONSENSUS", "MINIMAP2_POLISH"],   // re-imported under multiple names; edges reference the alias
      "module_path": "modules/nf-core/minimap2/align/main.nf",
      "tool": "minimap2_mulled",                              // FK into tools[].name
      "container": "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? '<sing-uri>' : '<other-uri>' }",  // verbatim directive
      "conda":     "${moduleDir}/environment.yml",                                                                                                  // verbatim directive
      "inputs":  [ { "name": "reads", "shape": "tuple(val(meta), path(reads))", "description": "...", "topic": null } ],
      "outputs": [ { "name": "paf",      "shape": "tuple(val(meta), path(\"*.paf\")) optional", "description": "...", "topic": null },
                   { "name": "versions", "shape": "path(\"versions.yml\")",                     "description": "tool versions YAML", "topic": null } ],
      "when": null,
      "script_excerpt": "\"\"\"\n$args\nminimap2 -t $task.cpus $reference $reads > ${prefix}.paf\n\"\"\"",   // verbatim script body; script_summary is the LLM pass's job
      "publish_dir": null }
  ],
  "subworkflows": [
    { "name": "FASTQ_TRIM_FASTP_FASTQC",
      "path": "subworkflows/nf-core/fastq_trim_fastp_fastqc/main.nf",
      "kind": "pipeline",
      "aliases": [],
      "calls": ["FASTP", "FASTQC_RAW", "FASTQC_TRIM"],
      "inputs": [], "outputs": [] },
    { "name": "PIPELINE_INITIALISATION",
      "path": "subworkflows/local/utils_nfcore_<name>_pipeline/main.nf",
      "kind": "utility",                       // composes free functions, no process invocations
      "aliases": [],
      "calls": [],
      "inputs": [], "outputs": [
        { "name": "samplesheet", "shape": "tuple(meta, path)", "description": "validated --input", "topic": null }
      ] }
  ],
  "workflow": {
    "name": "RNASEQ",
    "channels": [
      { "name": "ch_samplesheet",
        "source": "Channel.fromList(samplesheetToList(params.input, '...'))",
        "shape": "tuple(meta, [path,path])",
        "construct": "samplesheetToList",
        "from_param": "input",
        "required_runtime": false }
    ],
    "edges": [
      { "from": "ch_samplesheet", "to": "FASTP", "via": [] },
      { "from": "FASTP.out.reads", "to": "STAR_ALIGN",
        "via": ["map", "join"] }
    ],
    "conditionals": [
      { "guard": "params.skip_alignment", "branch": "alternate",
        "affects": ["STAR_ALIGN"] }
    ]
  },
  "test_candidates": [
    { "id": "tests/dfast.nf.test::-profile test_dfast",
      "kind": "nf-test",
      "name": "-profile test_dfast",
      "path": "tests/dfast.nf.test",
      "effective_profiles": ["test_dfast"],
      "params_delta": { "outdir": "$outputDir" },
      "inputs": [ /* TestDataRef-shaped */ ],
      "outputs": [ /* ExpectedOutputRef-shaped */ ],
      "execution_mode": "real",
      "scope": "primary",
      "disposition": "eligible",
      "rationale": "whole-pipeline nf-test case from tests/dfast.nf.test",
      "assert_workflow_success": true,
      "snapshot": {
        "captures":     ["succeeded_task_count", "versions_yml", "stable_names", "stable_paths"],
        "helpers":      ["getAllFilesFromDir", "removeNextflowVersion"],
        "ignore_files": ["tests/.nftignore", "tests/.nftignore_files_entirely"],
        "ignore_globs": [],
        "snap_path":    "tests/dfast.nf.test.snap"
      },
      "prose_assertions": [] }
  ],
  "test_selection": {
    "status": "needs-scope-choice",
    "selected_candidate_id": null,
    "rationale": "multiple primary-coverage cases remain incomparable"
  }
}
```

Field-name parity with gxy-sketches (`SketchSource`, `ToolSpec`, `TestDataRef`, `ExpectedOutputRef`) is intentional and load-bearing — see gxy-sketches-alignment §1-3.

### Procedure

The skill is **not a single LLM prompt** over the source tree. It is a small program with one or two embedded LLM calls. The split is:

- **Deterministic:** locate files, parse `nextflow.config` and `nextflow_schema.json`, regex-tokenize `process` blocks for typed fields (name, container, conda, declared IO channel names, `when:` guards, `publishDir`), read nf-core module `meta.yml` verbatim, enumerate `include { X } from '...'` for the call graph, resolve biocontainer image strings.
- **LLM-driven:** one-line `script_summary` of each process, over the verbatim `script_excerpt` the deterministic pass carries; reconciliation of operator-chained channel paths (`A | map | join(B) | groupTuple`) into the workflow `edges[]`, free-text `description` / `notes` fields, IO inference when `meta.yml` is absent and the script is the only signal.

Everything the schema demands as a typed enum or path is deterministic. Free-text fields are LLM. The schema enforces that boundary by typing.

#### 1. Detect pipeline shape

Branch shallow on layout:
- nf-core: `nextflow.config` declares `manifest.name = 'nf-core/...'`; `modules/nf-core/`, `subworkflows/nf-core/`, and `nextflow_schema.json` are present. Prefer `meta.yml` as IO ground truth.
- ad-hoc DSL2: no `nextflow_schema.json`, no module `meta.yml`. Falls back to `script:`-block IO inference. Consult component-nextflow-pipeline-anatomy when layout differs from nf-core conventions in ways these rules do not cover.
- DSL1: rare; emit the `source` block and exit early with a `warnings[]` entry. Out of scope for v1.

Real pipelines have **multiple named workflow blocks** — typically an anonymous `workflow {}` entrypoint in `main.nf` that wires `PIPELINE_INITIALISATION → NFCORE_<NAME> → PIPELINE_COMPLETION`, plus a substantive named workflow under `workflows/<name>.nf`. Selection rule for the primary `workflow`: pick the named workflow that invokes the most pipeline processes. The anonymous `workflow {}` glue and the `NFCORE_<NAME>` wrapper land in `subworkflows[]`, marked `kind: utility` and `kind: pipeline` respectively.

#### 2. Capture provenance

Populate `source` from `git remote get-url`, `git rev-parse HEAD` (or the user-supplied pin), `manifest.name` / `manifest.homePage` / `manifest.version` in `nextflow.config`, and `LICENSE` filename detection. `slug` is kebab of `<owner>-<repo>` for nf-core, kebab of repo basename otherwise.

#### 3. Parse parameters and profiles

Read `nextflow.config` `params { ... }` block for defaults. When `nextflow_schema.json` exists (nf-core), prefer it as the source of truth for `type`, `description`, and `required` — it is real JSON Schema, copy verbatim. Some params are computed at config-load time (for example `params.fasta = getGenomeAttribute('fasta')` in `main.nf`) and will not appear in `nextflow_schema.json`; include them with a description noting the dynamic source.

**`profiles[]`** — enumerate the `profiles { ... }` block and classify each entry by role. Track brace depth: a profile body contains nested `params { }` and `process { }` blocks, and a flat regex over the block emits those as profile names. The block is not always in `nextflow.config` — follow the root `includeConfig` chain (`epi2me-labs/wf-human-variation` declares all six profiles in `base.config`), and fall back to a bounded scan for other `*.config` files carrying a `profiles { }` block (`biocorecrg/MOP2` keeps them in `nextflow.global.config`, selected at launch with `-c`).

Classify from the profile *body*, resolving its `includeConfig` targets first — never from the name. The name heuristic fails both ways: `replikation/What_the_Phage`'s `test` profile only flips `fasta = true`, a mode switch with no test data, while `nf-core/sarek`'s `mutect` profile includes `conf/test_mutect2.config` and carries explicit test-data evidence. Profiles that pair a container engine with an executor are common outside nf-core — seven of `biocorecrg/MOP2`'s eight do, and `CRG-CNAG/CalliNGS-NF`'s `cluster` sets `process.container`, `singularity.enabled`, and `executor = 'crg'` in one block — so record every role, not just the first.

| `kinds` entry | evidence in the profile body |
|---|---|
| `test` | includes a `conf/test*.config`, or assigns a non-null `params.input` / `samplesheet` |
| `container` | `docker.enabled`, `conda.enabled`, `process.container`, `process.conda`, `process.arch`, … |
| `executor` | `executor.*`, `workDir`, `executor` / `queue` / `clusterOptions` inside `process { }`, an `aws {}` / `google {}` / `k8s {}` block |
| `resources` | process directives only — `memory`, `cpus`, `time`, `withLabel:` selectors |
| `mode` | params that change behavior without supplying input data (`nf-core/rnaseq`'s `prokaryotic`) |
| `dev` | `debug`, `gitpod` |
| `unknown` | no classifying signal |

`test`, `container`, `executor`, and `dev` accumulate. `mode` and `resources` are assigned **only when none of those matched** — their signals are much weaker, since nf-core's `docker` profile assigns `params.use_gpu` inside a ternary and most test profiles set process resource caps, so accumulating them would label most of a pipeline `mode`. Record the suppressed evidence in `signals[]` instead.

`kinds: ["test"]` is profile-level candidate evidence, not the test-selection decision. When pipeline-level nf-test-derived `test_candidates[]` entries exist, those test cases are the higher-fidelity unit because they also carry per-test parameter overrides; only fall back to an input-bearing profile when no pipeline-level nf-test case exists. The kind does not establish cost or representative coverage. `test_full` remains input-bearing but is commonly realistic-scale, while `test_minimal` / `test_tiny` may disable the science stages that the target translation must cover. Mode profiles can likewise encode scope choices that a target translation needs to surface; only execution-environment roles such as container and executor are intrinsically irrelevant to target workflow semantics.

Two limits worth stating plainly rather than leaving for a consumer to discover. `test` detection keys on nf-core input conventions, so an ad-hoc pipeline naming its inputs `reads` / `genome` gets no `test` profile even where one exists in spirit — across the fixture corpus all 103 `test` profiles are in the 16 nf-core pipelines and none in the 10 ad-hoc ones. And a `profiles { }` block outside the detected pipeline root is not found at all: `ncbi/egapx` keeps one in `ui/assets/config/user/`, which is launcher-side config for its Python wrapper rather than pipeline profiles.

`test_full` is `kinds: ["test"]` like any other; `includes` exposes the config family as a ranking signal rather than making it a separate kind. Populate `signals[]` with the evidence acted on so a surprising classification is auditable, and `source_path` with the config file that declared the profile.

#### 3.5. Resolve sample-sheet schemas

Sample-sheet inputs are the dominant structured-input idiom in modern nf-core pipelines and the most lossy thing to leave as prose inside `params[].description`. For each candidate sample-sheet parameter, populate one `sample_sheets[]` entry capturing the row schema deterministically. Discovery has three branches, recorded in `discovered_via`:

- `nf-schema`: the param's `nextflow_schema.json` entry has a `schema:` keyword pointing at a sibling JSON Schema file (`assets/schema_*.json`). Read that file. Each property in the row schema maps to one `SampleSheetColumn`. Preserve **property order**, not source-column order — `samplesheetToList()` emits columns in property order, and downstream channel item layout depends on it.
- `samplesheetToList`: the workflow imports `samplesheetToList` from nf-schema and calls it on the param. When the call cites a schema path, follow it. Without a schema path, emit the entry with `schema_path: null` and infer columns from `splitCsv`-shaped fallback if any; otherwise emit `columns: []` and a `warnings[]` note.
- `splitCsv`: a `Channel.fromPath(params.X).splitCsv(header: true)` materialization. Header inference only — emit columns by name, leave `type: string`, `kind` inferred from downstream `path()` consumption when traceable, else `meta`. Mark `discovered_via: splitCsv`.
- `ad-hoc`: pipeline-specific CSV/TSV parsing detected from script bodies (e.g. row-zero/row-one indexing). Emit a minimal entry with `columns: []` plus a `warnings[]` advisory; downstream skills will need to handle these by hand.

Column field rules:

- `kind`: `data` when nf-schema `format` is `file-path`/`directory-path`/`path` or when the column is annotated `meta:` is **absent** and the value is consumed as a `path()` downstream. `meta` otherwise (including all `meta: true` annotations and all non-path scalars). Nest the nf-schema `meta:` annotation here even when implicit — translation skills key on it to decide which columns become Galaxy `column_definitions[]` versus element/inner-collection slots.
- `type`: copy verbatim from the row schema (`string`/`integer`/`number`/`boolean`). Path columns are `string` with a `format` qualifier; do not collapse `path` into a synthetic type.
- `required`, `default`, `enum`, `pattern`, `exists`, `mimetype`, `description`: copy verbatim when present, leaving null/empty defaults otherwise.

This step does not reshape onto any target idiom (Galaxy `sample_sheet:paired` vs `list:paired` is not decided here). It records what the source pipeline declares; the variant choice belongs to nextflow-summary-to-galaxy-interface and nextflow-summary-to-cwl-interface.

#### 4. Enumerate processes

For each `process <NAME> { ... }` in `main.nf`, `workflows/`, `modules/**`, `subworkflows/**`:
- Pull `container`, `conda`, `publishDir`, `when:` directives **verbatim** into `processes[].container` / `processes[].conda`. Modern nf-core directives are ternary expressions (`workflow.containerEngine == 'singularity' ? <sing-uri> : <docker-uri>`) and file references (`${moduleDir}/environment.yml`); keep the directive text intact and resolve into `tools[]` separately (§5).
- Tokenize the `input:` and `output:` blocks for declared channel names and shapes — typed channels (`tuple val(meta), path(reads)`) become shape strings (`"tuple(meta, [path])"`); arity is preserved as a string, not structured.
- Sweep live `include { ... }` statements across the pipeline (`main.nf`, `workflows/`, `subworkflows/**`) to populate both `processes[].aliases` and `subworkflows[].aliases`. `include { MINIMAP2_ALIGN as MINIMAP2_CONSENSUS }` adds `MINIMAP2_CONSENSUS` to the canonical process; the same rule maps aliased subworkflow calls back to their canonical declaration. Calls and edges retain the alias spelling used by the source. Ignore includes inside line or block comments.
- Detect `topic: <name>` annotations on outputs (Nextflow 24+ channel topics — nf-core templates emit `tuple(val("${task.process}"), val('toolname'), eval(...)) topic: versions` for version aggregation). Record the topic name in `ChannelIO.topic`.
- Where `meta.yml` exists, **use it** for `description` and IO documentation rather than parsing the `script:` block.
- Carry the `script:`/`shell:`/`exec:` body verbatim into `script_excerpt` (dedented, truncated at 4000 characters, `stub:` dropped). For ad-hoc pipelines with no `meta.yml` this is the only evidence of what a process does.
- LLM call (one per process, batchable): summarize `script_excerpt` in one line into `script_summary`. Ask only for what the tool *does*. Deterministic runs omit `script_summary` rather than emitting a placeholder.

#### 5. Build the tool registry

Walk per-process `container` and `conda` directives. **Container directives are usually ternary** — extract both branches:

- The `singularity ?` branch typically yields an `https://depot.galaxyproject.org/singularity/<name>:<version>--<build>` URL → `tools[].singularity`.
- The fallthrough branch typically yields one of:
  - `quay.io/biocontainers/<name>:<version>--<build>` → `tools[].biocontainer`.
  - `biocontainers/<name>:<version>--<build>` (docker.io alias for the same biocontainer image) → `tools[].biocontainer` (same field; both forms are biocontainer images).
  - `community.wave.seqera.io/library/<name>:<version>--<digest>` or `https://community-cr-prod.seqera.io/.../sha256/<digest>/data` → `tools[].wave`.
  - Anything else → `tools[].docker`.

**Conda directives are usually file references** to `${moduleDir}/environment.yml`; read the file and extract its `dependencies:` list. Each `bioconda::<name>=<version>` entry becomes a `tools[]` entry with `tools[].bioconda` set to the original dependency string. Multi-tool environments are common (`minimap2` + `samtools` + `htslib`, `racon` + `multiqc`); keep every Bioconda dependency rather than selecting the first. Legacy literal-string directives (`conda "bioconda::<name>=<version>"`) feed the same field, as does the pre-`environment.yml` ternary form `conda (params.enable_conda ? "<spec>" : null)`.

Read the version off each spec rather than assuming one shape: `name=1.0` and `name==1.0` both pin exactly, a trailing `=<build>` is a conda build string and not part of the version, and an inexact constraint (`name>=1.17`) goes to `tools[].version_constraint` verbatim with `version` left `unknown`. A spec naming no version is legitimately unpinned — `unknown` with no constraint. Never let `unknown` into `tools[].versions[]`; it is the absence of a version, not a version. **A spec you cannot read must land in `warnings[]`** — the eval property is that every directive resolves *or* is flagged, and a directive that resolves in part satisfies neither unless the unread spec is named.

Tool name and version are typically derivable from any of the resolved fields. Deduplicate by `(name, version)` across processes; one entry per tool. `processes[].tool` is a foreign key into `tools[].name`; leave it null when a process declares several packages and none is "the" tool (`bioconda::xopen=1.1.0 bioconda::pysam=0.16.0`) rather than picking one arbitrarily. This block is the bridge to author-galaxy-tool-wrapper — it consumes container/conda info to choose or justify the UDT container.

#### 6. Reconcile the workflow DAG

Enumerate the top-level workflow's `include` statements and channel construction (`Channel.fromPath`, `Channel.fromFilePairs`, `Channel.fromList(samplesheetToList(...))`, `splitCsv`, `file()`/`files()`, `params.*`, `channel.empty()`, `channel.topic('<name>')`). For operator chains, the deterministic parser records the *literal* chain (`["map", "join", "groupTuple"]` in `via`). Reconciling chained operators into a coherent `from → to` edge is the second LLM call: given the literal chain, the source channel shape, and the downstream process's declared input shape, emit the resolved edge.

For each emitted `workflow.channels[]` entry, populate three classified fields alongside the verbatim `source`:

- **`construct`** — typed enum reflecting the channel's primary materialization factory or shape-determining operator. Selection precedence: (1) `samplesheetToList` when the chain contains `samplesheetToList(...)`; (2) `splitCsv` when the chain ends in `.splitCsv(header: true)` over a path; (3) otherwise the outermost factory (`Channel.fromPath` → `fromPath`, `Channel.fromFilePairs` → `fromFilePairs`, `Channel.fromList` → `fromList`, `file(...)` → `file`, `files(...)` → `files`, `Channel.of` → `of`, `Channel.value` → `value`, `Channel.empty` → `empty`, `Channel.topic` → `topic`); (4) `other` for derived/operator-only constructions.
- **`from_param`** — FK into `params[].name` when the construction expression directly references `params.X` (e.g. `Channel.fromPath(params.reads)`, `samplesheetToList(params.input, ...)`, `file(params.fasta)`). v1 is direct-only — one-hop Groovy bindings (`def reads = params.reads; Channel.fromPath(reads)`) are deferred to galaxyproject/foundry#211. Null when no direct reference, or when `construct` is not data-bearing (`empty`, `of`, `value`, `topic`, `other`).
- **`required_runtime`** — true when the construction chain ends in `.ifEmpty { error ... }` (or an equivalent imperative emptiness-throw guard). Captures runtime requiredness even when the param's nf-schema entry does not mark it required. False otherwise.

All three fields are syntactic: regex-level extraction over the construction expression, no LLM call.

Workflow-level conditionals (`if (params.skip_alignment) { ... }`) emit `conditionals[]` entries with the guard, the branch (`alternate` vs `default`), and the set of processes affected.

Subworkflows split into two kinds:
- `kind: pipeline` — invokes pipeline processes (data-flow contributor). The `NFCORE_<NAME>` wrapper and any nested `subworkflows/local/` that calls processes.
- `kind: utility` — composes free-function calls only (`paramsHelp`, `samplesheetToList`, `completionEmail`, `imNotification`). nf-core template subworkflows like `PIPELINE_INITIALISATION` and `PIPELINE_COMPLETION`. `Subworkflow.calls` is empty for utilities; their job is to produce channels (e.g. the validated samplesheet) the primary workflow consumes.

Parse only live `workflow` declarations. Definitions, includes, processes, and calls inside `// ...` or `/* ... */` comments are inert source text and must never contribute to the summary or overwrite a live declaration with the same name. Preserve each live subworkflow's `take:`, `emit:`, and distinct alias call spellings; populate `subworkflows[].aliases` on the canonical declaration.

Free-function calls in the workflow body itself (`paramsSummaryMap`, `softwareVersionsToYAML`, `methodsDescriptionText`) are not modeled as processes or subworkflows. Their channel outputs flow into the primary workflow's `channels[]`; the function names are nf-core template idiom, not pipeline-specific signal. Operator chains with deeply nested closures may produce edges flagged with low confidence in `notes`.

#### 7. Enumerate and select whole-pipeline test candidates

Emit one `test_candidates[]` list and one `test_selection` result. Do not emit a parallel singular fixture record or a second top-level nf-test list.

Apply nextflow-test-case-selection in fidelity order:

1. Statically enumerate every literal `test(...)` block inside a live pipeline-level `nextflow_pipeline {}` suite under `tests/`. Resolve `effective_profiles[]` from the root `nf-test.config` directive, file/test-level directives, then the optional caller override. Replacement and `+name` extension semantics matter; the descriptive test name is never configuration evidence. When Groovy generates cases dynamically, preserve one `dynamically-generated` aggregate candidate for the file and emit a warning.
2. If there are no pipeline nf-tests, emit every `profiles[]` entry whose body-derived `kinds` contains `test`; an explicit caller profile is a candidate even when its body could not be classified.
3. Otherwise emit one `pipeline-defaults` candidate from the selected entrypoint's declared defaults. Select it only if required launch parameters resolve.

For every candidate, populate its stable `id`, source `kind`, `path`, resolved profiles, `params_delta`, inputs, outputs, execution mode, scope, disposition, rationale, and assertion fields. Read each effective profile's static `includeConfig` targets and conventional `conf/<profile>.config`; merge per-test `when { params { ... } }` overrides last. Extract remote URLs and repo-relative paths into that candidate's own `inputs[]`.

When fixture fetching is enabled, hash each fetched remote file with SHA-1. When a test-data directory is provided, write the samplesheet and every referenced remote file under that directory using a deterministic URL-derived path and record that local filesystem path in `path` while preserving the original `url`. Fetch and expand samplesheets per candidate so alternatives do not share or overwrite one singular fixture list.

Each input/output entry follows `TestDataRef` / `ExpectedOutputRef` field names verbatim. The `path` vs `url` rules from gxy-sketches' `TestDataRef` carry over, with one extension: `path` may be the local fetched path for a remote URL. The "must be under `test_data/`" constraint does **not** — see gxy-sketches-alignment §1.

For nf-test-derived candidates:

- `name` = the description string passed to `test("...")`.
- `path` = repo-relative file path.
- `effective_profiles[]` = resolved nf-test/profile chain, never text scraped from `name`.
- `params_delta` = profile changes plus the `when { params { ... } }` block.
- `execution_mode` = `real`, `stub`, `mixed`, or `unknown` from the case options.
- `assert_workflow_success` = `true` when an `assert workflow.success` (or equivalent) clause is present.
- `snapshot` = structured `SnapshotFixture` when an `assert snapshot(...).match()` clause is present, else `null`. nf-core templates use a near-uniform snapshot pattern; extract:
  - `captures[]` = logical names of values passed into `snapshot(...)` (typical set: `succeeded_task_count`, `versions_yml`, `stable_names`, `stable_paths`).
  - `helpers[]` = nf-test helper functions invoked (`getAllFilesFromDir`, `removeNextflowVersion`, ...).
  - `ignore_files[]` = repo-relative paths passed as `ignoreFile:` to helpers (e.g. `tests/.nftignore`).
  - `ignore_globs[]` = inline `ignore: [...]` glob list from helpers.
  - `snap_path` = repo-relative path of the corresponding `.nf.test.snap` file.
- `prose_assertions[]` = any other complex/non-snapshot assertions, summarized to prose strings. Empty for snapshot-only tests (the common nf-core case).

Classify full-scale candidates as `reference-scale` and minimal/tiny/stub-only candidates as `bootstrap-only`; neither wins by name alone. Select exactly one primary candidate in `default.nf.test` or `main.nf.test`, otherwise the sole primary candidate. For profile fallbacks, prefer a body-classified primary profile named `test`, otherwise the sole primary profile. When several primary candidates remain, set `test_selection.status: needs-scope-choice`, keep every candidate, and select none.

Consult component-nextflow-testing when fixtures use a layout outside `conf/test.config` + nf-test (e.g. legacy `test/` scripts, external test harnesses) or when assertions are non-snapshot equality / regex / `containsString` checks.

#### 8. Validate and emit

Validate the assembled object before emitting: run `foundry validate-summary-nextflow summary-nextflow.json`. The subcommand is shipped by `@galaxy-foundry/gxwf-foundry` and can be invoked from npm with `npx --package @galaxy-foundry/gxwf-foundry foundry validate-summary-nextflow summary-nextflow.json`. The standalone `summarize-nextflow` bin (from `@galaxy-foundry/summarize-nextflow`) self-validates by default and is the better gate when the skill is also producing the summary. On schema failure, the skill should fail loud — the downstream skills bind to the schema and will produce worse errors later. `additionalProperties: false` at every level catches drift early; do not add extra fields to work around a mismatch.

### Caveats baked into the procedure

The procedure assumes — and the skill must surface in `warnings[]` when relevant — the following NF realities:

- **DSL1 pipelines are out of scope.** Detected via the absence of DSL2 syntax (`workflow { ... }` block); emit a single warning and exit with the provenance block only.
- **`meta.yml` may lie.** nf-core module `meta.yml` is hand-authored and can drift from the actual `script:` IO. When the LLM-inferred IO disagrees with `meta.yml`, prefer `meta.yml` and surface the disagreement as a warning rather than overriding it.
- **Channel shapes are strings, not structured types.** `"tuple(meta, [path,path])"` is enough for downstream skills to reason about; structured channel typing is a research project. Downstream skills that need structure must parse the string.
- **Operator chains are summarized, not executed.** The LLM reconciliation pass is best-effort. Workflows with deeply nested closures (`map { ... }` with substantial Groovy logic) may produce edges flagged with low confidence in `notes`.
- **`include` aliasing is followed one level.** `include { FASTP as TRIM_PROC } from '...'` resolves to `FASTP` in `processes[].name` and the alias is recorded in the call graph. Multi-level aliasing chains are not chased.
- **Test-fixture fetching is bounded.** Without explicit fixture fetching, record URL, role, filetype, and expected SHA-1 if present; do not download content for validation. When fixture fetching is requested, fetch only selected-profile URL params and direct remote URLs discovered in fetched samplesheets. Do not recursively crawl archives or arbitrary generated paths.

### Reference dispatch

- summary-nextflow — always validate output against this schema before emitting.
- component-nextflow-pipeline-anatomy — consult on ad-hoc DSL2 layouts that do not match nf-core conventions, or on workflow-block patterns the multi-workflow selection rule does not resolve.
- component-nextflow-containers-and-envs — consult on container/conda directives outside the resolver patterns above, including mulled-v2, custom registries, env modules, Wave, and multi-dependency `environment.yml` files.
- component-nextflow-testing — consult on test fixture layouts outside `conf/test.config` + nf-test, or on snapshot/assertion patterns the structured fallback does not capture well.
- nextflow-test-case-selection — consult while enumerating `test_candidates[]` and deciding `test_selection`; a profile override changes candidate configuration but does not identify a test case.

### Non-goals

- **Subset summarization.** Whole-pipeline only. A single-subworkflow summarizer might land later, but the schema and downstream skills assume the whole-pipeline shape today.
- **Translation to a target idiom.** This skill does not produce Galaxy collections, CWL scatter, or any target-shaped data flow. Those live in nextflow-summary-to-galaxy-interface, nextflow-summary-to-galaxy-data-flow, nextflow-summary-to-cwl-interface, and nextflow-summary-to-cwl-data-flow.
- **Tool wrapping.** Container/conda info is captured for author-galaxy-tool-wrapper to consume; this skill never authors a wrapper.
- **Test execution.** Fixtures are described, not run. run-workflow-test owns execution.
- **Schema evolution.** The schema at summary-nextflow is v1, draft. Adding fields requires evaluating against the canonical exemplars (rnaseq, sarek, one ad-hoc DSL2 pipeline) before merging.

## Feedback Mode

- Feedback mode is off unless the caller explicitly enables `--feedback` or supplies a feedback-ledger path.
- When enabled, read `_feedback.md` before doing the work and use its registered `foundry-feedback.ledger.yml` protocol.
- Preserve harness-owned run and phase state. Append only concrete observations about a canonical Foundry source asset or a related project that this run showed to be at fault; do not put ordinary workflow requirements in this ledger.
- Before reporting completion, make one explicit pass over the work you just did. Do not ask yourself whether anything was unclear — recall what happened: where you guessed at something the instructions should have settled, needed information this bundle does not carry, hit an instruction that contradicted another or contradicted the artifacts in front of you, used a packaged reference that did not cover your case, or did something the procedure never describes.
- Append an entry for each such event that clears the protocol's bar. If none do, append nothing and report `no feedback` explicitly. Silence and a clean pass are not the same thing, and nothing downstream can tell them apart unless you say which one it was.
- Pass the same ledger path to any subagent used for this work, and merge updates serially so one writer cannot overwrite another.

## Runtime Notes

- Do not read Foundry source files at runtime; use only files packaged in this skill bundle and user-supplied artifacts.
- Preserve declared artifact filenames unless the user or harness supplies explicit paths.
- Carry unresolved assumptions into the output artifact instead of silently inventing missing source evidence.