Repository: tektoncd/pipeline (language: go)

Issue:
Matrix: invalid param type when consuming results from CustomRun on task matrix.params field
# Expected Behavior
I expect the validation to pass and the tasks to run successfully.

# Actual Behavior
A reconciliation error is preventing the pipeline from executing successfully. 
Received an error message stating:
```
'[User error] Failed to validate matrix "parameters of type array only
      are allowed, but param \"arch\" has type \"string\" in pipelineTask \"matrix-job\""
      with error %!w(MISSING)'
```

# Steps to Reproduce the Problem
We have the pipelinerun data segment as following:  
```yaml
  pipelineSpec:
    displayName: test
    tasks:
    - displayName: prepare
      name: prepare
      taskSpec:
        steps:
        - args:
          - -step-name=arch
          command:
          - /user/bin/entry
          image: busybox
          name: arch
    - name: matrix-job-eval
      params:
      - name: expression
        value: '{"matrix-param-arch":"${{jobs.prepare.outputs.arch}}"}'
      runAfter:
      - prepare
      taskRef:
        ## matrix-job-eval is a customrun
        apiVersion: spel.taskline.dev/v1alpha1
        kind: SpEL
    - displayName: matrix-job
      matrix:
        params:
        - name: arch
          value: $(tasks.matrix-job-eval.results.matrix-param-arch)
      name: matrix-job
      runAfter:
      - matrix-job-eval
      taskSpec:
        params:
        - name: arch
          type: string
        spec: null
        steps:
        - args:
          - arch=$(params.arch)
          command:
          - /user/bin/consume-arch
          image: busybox
          name: step-0
```
Note: it's not a full resource yaml data for the consideration of sensitive data, but I believe it's enough to show the problem.   

What's going on here is that `matrix-job-eval` task is a customrun and will emit a array semantic result:   
```yaml
  results:
  - name: matrix-param-arch
    value: '[ "arm", "amd64" ]'
```
We expect `matrix-job` task could fan out to 2 taskruns according to the matrix-param-arch value, but we just got the user error I mentioned above.

# Additional Info

- Kubernetes version:

  **Output of `kubectl version`:**

```
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:38:33Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"darwin/arm64"}
Server Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.3-alibaba.126-dc14a4e28076cf", GitCommit:"dc14a4e28076cfaf6c0bf010c837eec6029bd97f", GitTreeState:"clean", BuildDate:"2023-11-06T11:02:05Z", GoVersion:"go1.16.9", Compiler:"gc", Platform:"linux/amd64"}
```

- Tekton Pipeline version:

  **Output of `tkn version` or `kubectl get pods -n tekton-pipelines -l app=tekton-pipelines-controller -o=jsonpath='{.items[0].metadata.labels.version}'`**

```
v0.57.0
```


<!-- Any other additional information -->



Discussion / hints:

Fix patch:
diff --git a/pkg/reconciler/pipelinerun/resources/resultrefresolution.go b/pkg/reconciler/pipelinerun/resources/resultrefresolution.go
index 73d7f9cf29a..5fa2217072d 100644
--- a/pkg/reconciler/pipelinerun/resources/resultrefresolution.go
+++ b/pkg/reconciler/pipelinerun/resources/resultrefresolution.go
@@ -17,6 +17,7 @@ limitations under the License.
 package resources
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"sort"
@@ -164,13 +165,28 @@ func resolveCustomResultRef(customRuns []*v1beta1.CustomRun, resultRef *v1.Resul
 		return nil, err
 	}
 	return &ResolvedResultRef{
-		Value:           *v1.NewStructuredValues(runValue),
+		Value:           *paramValueFromCustomRunResult(runValue),
 		FromTaskRun:     "",
 		FromRun:         runName,
 		ResultReference: *resultRef,
 	}, nil
 }
 
+func paramValueFromCustomRunResult(result string) *v1.ParamValue {
+	var arrayResult []string
+	// for fan out array result, which is represented as string, we should make it to array type param value
+	if err := json.Unmarshal([]byte(result), &arrayResult); err == nil && len(arrayResult) > 0 {
+		if len(arrayResult) > 1 {
+			return v1.NewStructuredValues(arrayResult[0], arrayResult[1:]...)
+		}
+		return &v1.ParamValue{
+			Type:     v1.ParamTypeArray,
+			ArrayVal: []string{arrayResult[0]},
+		}
+	}
+	return v1.NewStructuredValues(result)
+}
+
 func resolveResultRef(taskRuns []*v1.TaskRun, resultRef *v1.ResultRef) (*ResolvedResultRef, error) {
 	taskRun := taskRuns[0]
 	taskRunName := taskRun.Name


Test patch:
diff --git a/pkg/reconciler/pipelinerun/resources/resultrefresolution_test.go b/pkg/reconciler/pipelinerun/resources/resultrefresolution_test.go
index ef18e506b13..284bf5704ae 100644
--- a/pkg/reconciler/pipelinerun/resources/resultrefresolution_test.go
+++ b/pkg/reconciler/pipelinerun/resources/resultrefresolution_test.go
@@ -841,3 +841,53 @@ func TestValidateArrayResultsIndex(t *testing.T) {
 		})
 	}
 }
+
+func TestParamValueFromCustomRunResult(t *testing.T) {
+	type args struct {
+		result string
+	}
+	tests := []struct {
+		name string
+		args args
+		want *v1.ParamValue
+	}{
+		{
+			name: "multiple array elements result",
+			args: args{
+				result: `["amd64", "arm64"]`,
+			},
+			want: &v1.ParamValue{
+				Type:     "array",
+				ArrayVal: []string{"amd64", "arm64"},
+			},
+		},
+		{
+			name: "single array elements result",
+			args: args{
+				result: `[ "amd64" ]`,
+			},
+			want: &v1.ParamValue{
+				Type:     "array",
+				ArrayVal: []string{"amd64"},
+			},
+		},
+		{
+			name: "simple string result",
+			args: args{
+				result: "amd64",
+			},
+			want: &v1.ParamValue{
+				Type:      "string",
+				StringVal: "amd64",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := paramValueFromCustomRunResult(tt.args.result)
+			if d := cmp.Diff(tt.want, got); d != "" {
+				t.Fatalf("paramValueFromCustomRunResult %s", diff.PrintWantGot(d))
+			}
+		})
+	}
+}